StatsCompanionService.java revision 1a0a941c20eb746868d0de52e3806f69c74d335f
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.server.stats; 17 18import android.annotation.Nullable; 19import android.app.ActivityManagerInternal; 20import android.app.AlarmManager; 21import android.app.PendingIntent; 22import android.app.ProcessMemoryState; 23import android.app.StatsManager; 24import android.bluetooth.BluetoothActivityEnergyInfo; 25import android.bluetooth.BluetoothAdapter; 26import android.bluetooth.UidTraffic; 27import android.content.BroadcastReceiver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.IntentSender; 32import android.content.pm.PackageInfo; 33import android.content.pm.PackageManager; 34import android.content.pm.UserInfo; 35import android.net.NetworkStats; 36import android.net.wifi.IWifiManager; 37import android.net.wifi.WifiActivityEnergyInfo; 38import android.os.BatteryStatsInternal; 39import android.os.Binder; 40import android.os.Bundle; 41import android.os.Environment; 42import android.os.IBinder; 43import android.os.IStatsCompanionService; 44import android.os.IStatsManager; 45import android.os.Parcelable; 46import android.os.Process; 47import android.os.RemoteException; 48import android.os.ServiceManager; 49import android.os.StatFs; 50import android.os.StatsDimensionsValue; 51import android.os.StatsLogEventWrapper; 52import android.os.SynchronousResultReceiver; 53import android.os.SystemClock; 54import android.os.UserHandle; 55import android.os.UserManager; 56import android.telephony.ModemActivityInfo; 57import android.telephony.TelephonyManager; 58import android.util.Slog; 59import android.util.StatsLog; 60 61import com.android.internal.annotations.GuardedBy; 62import com.android.internal.net.NetworkStatsFactory; 63import com.android.internal.os.KernelCpuSpeedReader; 64import com.android.internal.os.KernelUidCpuTimeReader; 65import com.android.internal.os.KernelUidCpuClusterTimeReader; 66import com.android.internal.os.KernelUidCpuActiveTimeReader; 67import com.android.internal.os.KernelUidCpuFreqTimeReader; 68import com.android.internal.os.KernelWakelockReader; 69import com.android.internal.os.KernelWakelockStats; 70import com.android.internal.os.PowerProfile; 71import com.android.server.LocalServices; 72import com.android.server.SystemService; 73 74import java.io.IOException; 75import java.util.ArrayList; 76import java.util.Arrays; 77import java.util.List; 78import java.util.Map; 79import java.util.concurrent.TimeoutException; 80 81/** 82 * Helper service for statsd (the native stats management service in cmds/statsd/). 83 * Used for registering and receiving alarms on behalf of statsd. 84 * 85 * @hide 86 */ 87public class StatsCompanionService extends IStatsCompanionService.Stub { 88 /** 89 * How long to wait on an individual subsystem to return its stats. 90 */ 91 private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; 92 93 public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity"; 94 95 static final String TAG = "StatsCompanionService"; 96 static final boolean DEBUG = false; 97 98 public static final int CODE_DATA_BROADCAST = 1; 99 public static final int CODE_SUBSCRIBER_BROADCAST = 1; 100 101 private final Context mContext; 102 private final AlarmManager mAlarmManager; 103 @GuardedBy("sStatsdLock") 104 private static IStatsManager sStatsd; 105 private static final Object sStatsdLock = new Object(); 106 107 private final PendingIntent mAnomalyAlarmIntent; 108 private final PendingIntent mPullingAlarmIntent; 109 private final PendingIntent mPeriodicAlarmIntent; 110 private final BroadcastReceiver mAppUpdateReceiver; 111 private final BroadcastReceiver mUserUpdateReceiver; 112 private final ShutdownEventReceiver mShutdownEventReceiver; 113 private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); 114 private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); 115 private IWifiManager mWifiManager = null; 116 private TelephonyManager mTelephony = null; 117 private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath()); 118 private final StatFs mStatFsSystem = 119 new StatFs(Environment.getRootDirectory().getAbsolutePath()); 120 private final StatFs mStatFsTemp = 121 new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); 122 123 private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); 124 private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; 125 private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader = 126 new KernelUidCpuFreqTimeReader(); 127 private KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader = 128 new KernelUidCpuActiveTimeReader(); 129 private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader = 130 new KernelUidCpuClusterTimeReader(); 131 132 public StatsCompanionService(Context context) { 133 super(); 134 mContext = context; 135 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 136 137 mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0, 138 new Intent(mContext, AnomalyAlarmReceiver.class), 0); 139 mPullingAlarmIntent = PendingIntent.getBroadcast( 140 mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0); 141 mPeriodicAlarmIntent = PendingIntent.getBroadcast( 142 mContext, 0, new Intent(mContext, PeriodicAlarmReceiver.class), 0); 143 mAppUpdateReceiver = new AppUpdateReceiver(); 144 mUserUpdateReceiver = new BroadcastReceiver() { 145 @Override 146 public void onReceive(Context context, Intent intent) { 147 synchronized (sStatsdLock) { 148 sStatsd = fetchStatsdService(); 149 if (sStatsd == null) { 150 Slog.w(TAG, "Could not access statsd for UserUpdateReceiver"); 151 return; 152 } 153 try { 154 // Pull the latest state of UID->app name, version mapping. 155 // Needed since the new user basically has a version of every app. 156 informAllUidsLocked(context); 157 } catch (RemoteException e) { 158 Slog.e(TAG, "Failed to inform statsd latest update of all apps", e); 159 forgetEverything(); 160 } 161 } 162 } 163 }; 164 mShutdownEventReceiver = new ShutdownEventReceiver(); 165 if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED."); 166 PowerProfile powerProfile = new PowerProfile(context); 167 final int numClusters = powerProfile.getNumCpuClusters(); 168 mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters]; 169 int firstCpuOfCluster = 0; 170 for (int i = 0; i < numClusters; i++) { 171 final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i); 172 mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster, 173 numSpeedSteps); 174 firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i); 175 } 176 // use default throttling in 177 // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader 178 mKernelUidCpuFreqTimeReader.setThrottleInterval(0); 179 long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile); 180 mKernelUidCpuClusterTimeReader.setThrottleInterval(0); 181 mKernelUidCpuActiveTimeReader.setThrottleInterval(0); 182 } 183 184 @Override 185 public void sendDataBroadcast(IBinder intentSenderBinder) { 186 enforceCallingPermission(); 187 IntentSender intentSender = new IntentSender(intentSenderBinder); 188 Intent intent = new Intent(); 189 try { 190 intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null); 191 } catch (IntentSender.SendIntentException e) { 192 Slog.w(TAG, "Unable to send using IntentSender"); 193 } 194 } 195 196 @Override 197 public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey, 198 long subscriptionId, long subscriptionRuleId, 199 String[] cookies, 200 StatsDimensionsValue dimensionsValue) { 201 enforceCallingPermission(); 202 IntentSender intentSender = new IntentSender(intentSenderBinder); 203 Intent intent = new Intent() 204 .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid) 205 .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey) 206 .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId) 207 .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId) 208 .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue); 209 210 ArrayList<String> cookieList = new ArrayList<>(cookies.length); 211 for (String cookie : cookies) { cookieList.add(cookie); } 212 intent.putStringArrayListExtra( 213 StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList); 214 215 if (DEBUG) { 216 Slog.d(TAG, String.format( 217 "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}", 218 configUid, configKey, subscriptionId, subscriptionRuleId, 219 Arrays.toString(cookies), dimensionsValue)); 220 } 221 try { 222 intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null); 223 } catch (IntentSender.SendIntentException e) { 224 Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid 225 + "; presumably it had been cancelled."); 226 } 227 } 228 229 private final static int[] toIntArray(List<Integer> list) { 230 int[] ret = new int[list.size()]; 231 for (int i = 0; i < ret.length; i++) { 232 ret[i] = list.get(i); 233 } 234 return ret; 235 } 236 237 private final static long[] toLongArray(List<Long> list) { 238 long[] ret = new long[list.size()]; 239 for (int i = 0; i < ret.length; i++) { 240 ret[i] = list.get(i); 241 } 242 return ret; 243 } 244 245 // Assumes that sStatsdLock is held. 246 @GuardedBy("sStatsdLock") 247 private final void informAllUidsLocked(Context context) throws RemoteException { 248 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 249 PackageManager pm = context.getPackageManager(); 250 final List<UserInfo> users = um.getUsers(true); 251 if (DEBUG) { 252 Slog.d(TAG, "Iterating over " + users.size() + " profiles."); 253 } 254 255 List<Integer> uids = new ArrayList<>(); 256 List<Long> versions = new ArrayList<>(); 257 List<String> apps = new ArrayList<>(); 258 259 // Add in all the apps for every user/profile. 260 for (UserInfo profile : users) { 261 List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id); 262 for (int j = 0; j < pi.size(); j++) { 263 if (pi.get(j).applicationInfo != null) { 264 uids.add(pi.get(j).applicationInfo.uid); 265 versions.add(pi.get(j).getLongVersionCode()); 266 apps.add(pi.get(j).packageName); 267 } 268 } 269 } 270 sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new 271 String[apps.size()])); 272 if (DEBUG) { 273 Slog.d(TAG, "Sent data for " + uids.size() + " apps"); 274 } 275 } 276 277 private final static class AppUpdateReceiver extends BroadcastReceiver { 278 @Override 279 public void onReceive(Context context, Intent intent) { 280 /** 281 * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid 282 * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag. 283 * If we can't find the value for EXTRA_REPLACING, we default to false. 284 */ 285 if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) 286 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 287 return; // Keep only replacing or normal add and remove. 288 } 289 if (DEBUG) Slog.d(TAG, "StatsCompanionService noticed an app was updated."); 290 synchronized (sStatsdLock) { 291 if (sStatsd == null) { 292 Slog.w(TAG, "Could not access statsd to inform it of an app update"); 293 return; 294 } 295 try { 296 if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) { 297 Bundle b = intent.getExtras(); 298 int uid = b.getInt(Intent.EXTRA_UID); 299 boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 300 if (!replacing) { 301 // Don't bother sending an update if we're right about to get another 302 // intent for the new version that's added. 303 PackageManager pm = context.getPackageManager(); 304 String app = intent.getData().getSchemeSpecificPart(); 305 sStatsd.informOnePackageRemoved(app, uid); 306 } 307 } else { 308 PackageManager pm = context.getPackageManager(); 309 Bundle b = intent.getExtras(); 310 int uid = b.getInt(Intent.EXTRA_UID); 311 String app = intent.getData().getSchemeSpecificPart(); 312 PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER); 313 sStatsd.informOnePackage(app, uid, pi.getLongVersionCode()); 314 } 315 } catch (Exception e) { 316 Slog.w(TAG, "Failed to inform statsd of an app update", e); 317 } 318 } 319 } 320 } 321 322 public final static class AnomalyAlarmReceiver extends BroadcastReceiver { 323 @Override 324 public void onReceive(Context context, Intent intent) { 325 Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time " 326 + System.currentTimeMillis() + "ms."); 327 synchronized (sStatsdLock) { 328 if (sStatsd == null) { 329 Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); 330 return; 331 } 332 try { 333 // Two-way call to statsd to retain AlarmManager wakelock 334 sStatsd.informAnomalyAlarmFired(); 335 } catch (RemoteException e) { 336 Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e); 337 } 338 } 339 // AlarmManager releases its own wakelock here. 340 } 341 } 342 343 public final static class PullingAlarmReceiver extends BroadcastReceiver { 344 @Override 345 public void onReceive(Context context, Intent intent) { 346 if (DEBUG) 347 Slog.d(TAG, "Time to poll something."); 348 synchronized (sStatsdLock) { 349 if (sStatsd == null) { 350 Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing."); 351 return; 352 } 353 try { 354 // Two-way call to statsd to retain AlarmManager wakelock 355 sStatsd.informPollAlarmFired(); 356 } catch (RemoteException e) { 357 Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e); 358 } 359 } 360 } 361 } 362 363 public final static class PeriodicAlarmReceiver extends BroadcastReceiver { 364 @Override 365 public void onReceive(Context context, Intent intent) { 366 if (DEBUG) 367 Slog.d(TAG, "Time to poll something."); 368 synchronized (sStatsdLock) { 369 if (sStatsd == null) { 370 Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing."); 371 return; 372 } 373 try { 374 // Two-way call to statsd to retain AlarmManager wakelock 375 sStatsd.informAlarmForSubscriberTriggeringFired(); 376 } catch (RemoteException e) { 377 Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e); 378 } 379 } 380 // AlarmManager releases its own wakelock here. 381 } 382 } 383 384 public final static class ShutdownEventReceiver extends BroadcastReceiver { 385 @Override 386 public void onReceive(Context context, Intent intent) { 387 /** 388 * Skip immediately if intent is not relevant to device shutdown. 389 */ 390 if (!intent.getAction().equals(Intent.ACTION_REBOOT) 391 && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN) 392 && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) { 393 return; 394 } 395 396 Slog.i(TAG, "StatsCompanionService noticed a shutdown."); 397 synchronized (sStatsdLock) { 398 if (sStatsd == null) { 399 Slog.w(TAG, "Could not access statsd to inform it of a shutdown event."); 400 return; 401 } 402 try { 403 sStatsd.writeDataToDisk(); 404 } catch (Exception e) { 405 Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e); 406 } 407 } 408 } 409 } 410 411 @Override // Binder call 412 public void setAnomalyAlarm(long timestampMs) { 413 enforceCallingPermission(); 414 if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs); 415 final long callingToken = Binder.clearCallingIdentity(); 416 try { 417 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will 418 // only fire when it awakens. 419 // This alarm is inexact, leaving its exactness completely up to the OS optimizations. 420 // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm. 421 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, mAnomalyAlarmIntent); 422 } finally { 423 Binder.restoreCallingIdentity(callingToken); 424 } 425 } 426 427 @Override // Binder call 428 public void cancelAnomalyAlarm() { 429 enforceCallingPermission(); 430 if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm"); 431 final long callingToken = Binder.clearCallingIdentity(); 432 try { 433 mAlarmManager.cancel(mAnomalyAlarmIntent); 434 } finally { 435 Binder.restoreCallingIdentity(callingToken); 436 } 437 } 438 439 @Override // Binder call 440 public void setAlarmForSubscriberTriggering(long timestampMs) { 441 enforceCallingPermission(); 442 if (DEBUG) 443 Slog.d(TAG, "Setting periodic alarm at " + timestampMs); 444 final long callingToken = Binder.clearCallingIdentity(); 445 try { 446 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will 447 // only fire when it awakens. 448 // This alarm is inexact, leaving its exactness completely up to the OS optimizations. 449 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent); 450 } finally { 451 Binder.restoreCallingIdentity(callingToken); 452 } 453 } 454 455 @Override // Binder call 456 public void cancelAlarmForSubscriberTriggering() { 457 enforceCallingPermission(); 458 if (DEBUG) 459 Slog.d(TAG, "Cancelling periodic alarm"); 460 final long callingToken = Binder.clearCallingIdentity(); 461 try { 462 mAlarmManager.cancel(mPeriodicAlarmIntent); 463 } finally { 464 Binder.restoreCallingIdentity(callingToken); 465 } 466 } 467 468 @Override // Binder call 469 public void setPullingAlarm(long nextPullTimeMs) { 470 enforceCallingPermission(); 471 if (DEBUG) 472 Slog.d(TAG, 473 "Setting pulling alarm in about " + (nextPullTimeMs - SystemClock.elapsedRealtime())); 474 final long callingToken = Binder.clearCallingIdentity(); 475 try { 476 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will 477 // only fire when it awakens. 478 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, mPullingAlarmIntent); 479 } finally { 480 Binder.restoreCallingIdentity(callingToken); 481 } 482 } 483 484 @Override // Binder call 485 public void cancelPullingAlarm() { 486 enforceCallingPermission(); 487 if (DEBUG) 488 Slog.d(TAG, "Cancelling pulling alarm"); 489 final long callingToken = Binder.clearCallingIdentity(); 490 try { 491 mAlarmManager.cancel(mPullingAlarmIntent); 492 } finally { 493 Binder.restoreCallingIdentity(callingToken); 494 } 495 } 496 497 private void addNetworkStats( 498 int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) { 499 int size = stats.size(); 500 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 501 NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling 502 for (int j = 0; j < size; j++) { 503 stats.getValues(j, entry); 504 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5); 505 e.writeInt(entry.uid); 506 if (withFGBG) { 507 e.writeInt(entry.set); 508 } 509 e.writeLong(entry.rxBytes); 510 e.writeLong(entry.rxPackets); 511 e.writeLong(entry.txBytes); 512 e.writeLong(entry.txPackets); 513 ret.add(e); 514 } 515 } 516 517 /** 518 * Allows rollups per UID but keeping the set (foreground/background) slicing. 519 * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java 520 */ 521 private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) { 522 final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1); 523 524 final NetworkStats.Entry entry = new NetworkStats.Entry(); 525 entry.iface = NetworkStats.IFACE_ALL; 526 entry.tag = NetworkStats.TAG_NONE; 527 entry.metered = NetworkStats.METERED_ALL; 528 entry.roaming = NetworkStats.ROAMING_ALL; 529 530 int size = stats.size(); 531 NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values 532 for (int i = 0; i < size; i++) { 533 stats.getValues(i, recycle); 534 535 // Skip specific tags, since already counted in TAG_NONE 536 if (recycle.tag != NetworkStats.TAG_NONE) continue; 537 538 entry.set = recycle.set; // Allows slicing by background/foreground 539 entry.uid = recycle.uid; 540 entry.rxBytes = recycle.rxBytes; 541 entry.rxPackets = recycle.rxPackets; 542 entry.txBytes = recycle.txBytes; 543 entry.txPackets = recycle.txPackets; 544 // Operations purposefully omitted since we don't use them for statsd. 545 ret.combineValues(entry); 546 } 547 return ret; 548 } 549 550 /** 551 * Helper method to extract the Parcelable controller info from a 552 * SynchronousResultReceiver. 553 */ 554 private static <T extends Parcelable> T awaitControllerInfo( 555 @Nullable SynchronousResultReceiver receiver) { 556 if (receiver == null) { 557 return null; 558 } 559 560 try { 561 final SynchronousResultReceiver.Result result = 562 receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); 563 if (result.bundle != null) { 564 // This is the final destination for the Bundle. 565 result.bundle.setDefusable(true); 566 567 final T data = result.bundle.getParcelable( 568 RESULT_RECEIVER_CONTROLLER_KEY); 569 if (data != null) { 570 return data; 571 } 572 } 573 Slog.e(TAG, "no controller energy info supplied for " + receiver.getName()); 574 } catch (TimeoutException e) { 575 Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); 576 } 577 return null; 578 } 579 580 private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) { 581 final KernelWakelockStats wakelockStats = 582 mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats); 583 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 584 for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { 585 String name = ent.getKey(); 586 KernelWakelockStats.Entry kws = ent.getValue(); 587 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4); 588 e.writeString(name); 589 e.writeInt(kws.mCount); 590 e.writeInt(kws.mVersion); 591 e.writeLong(kws.mTotalTime); 592 pulledData.add(e); 593 } 594 } 595 596 private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) { 597 long token = Binder.clearCallingIdentity(); 598 try { 599 // TODO: Consider caching the following call to get BatteryStatsInternal. 600 BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); 601 String[] ifaces = bs.getWifiIfaces(); 602 if (ifaces.length == 0) { 603 return; 604 } 605 NetworkStatsFactory nsf = new NetworkStatsFactory(); 606 // Combine all the metrics per Uid into one record. 607 NetworkStats stats = 608 nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null) 609 .groupedByUid(); 610 addNetworkStats(tagId, pulledData, stats, false); 611 } catch (java.io.IOException e) { 612 Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); 613 } finally { 614 Binder.restoreCallingIdentity(token); 615 } 616 } 617 618 private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) { 619 long token = Binder.clearCallingIdentity(); 620 try { 621 BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); 622 String[] ifaces = bs.getWifiIfaces(); 623 if (ifaces.length == 0) { 624 return; 625 } 626 NetworkStatsFactory nsf = new NetworkStatsFactory(); 627 NetworkStats stats = rollupNetworkStatsByFGBG( 628 nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)); 629 addNetworkStats(tagId, pulledData, stats, true); 630 } catch (java.io.IOException e) { 631 Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); 632 } finally { 633 Binder.restoreCallingIdentity(token); 634 } 635 } 636 637 private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) { 638 long token = Binder.clearCallingIdentity(); 639 try { 640 BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); 641 String[] ifaces = bs.getMobileIfaces(); 642 if (ifaces.length == 0) { 643 return; 644 } 645 NetworkStatsFactory nsf = new NetworkStatsFactory(); 646 // Combine all the metrics per Uid into one record. 647 NetworkStats stats = 648 nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null) 649 .groupedByUid(); 650 addNetworkStats(tagId, pulledData, stats, false); 651 } catch (java.io.IOException e) { 652 Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); 653 } finally { 654 Binder.restoreCallingIdentity(token); 655 } 656 } 657 658 private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) { 659 BluetoothActivityEnergyInfo info = pullBluetoothData(); 660 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 661 for (UidTraffic traffic : info.getUidTraffic()) { 662 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3); 663 e.writeInt(traffic.getUid()); 664 e.writeLong(traffic.getRxBytes()); 665 e.writeLong(traffic.getTxBytes()); 666 pulledData.add(e); 667 } 668 } 669 670 private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) { 671 long token = Binder.clearCallingIdentity(); 672 try { 673 BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); 674 String[] ifaces = bs.getMobileIfaces(); 675 if (ifaces.length == 0) { 676 return; 677 } 678 NetworkStatsFactory nsf = new NetworkStatsFactory(); 679 NetworkStats stats = rollupNetworkStatsByFGBG( 680 nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)); 681 addNetworkStats(tagId, pulledData, stats, true); 682 } catch (java.io.IOException e) { 683 Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); 684 } finally { 685 Binder.restoreCallingIdentity(token); 686 } 687 } 688 689 private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) { 690 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 691 for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { 692 long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute(); 693 if (clusterTimeMs != null) { 694 for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) { 695 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3); 696 e.writeInt(cluster); 697 e.writeInt(speed); 698 e.writeLong(clusterTimeMs[speed]); 699 pulledData.add(e); 700 } 701 } 702 } 703 } 704 705 private void pullKernelUidCpuTime(int tagId, List<StatsLogEventWrapper> pulledData) { 706 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 707 mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> { 708 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3); 709 e.writeInt(uid); 710 e.writeLong(userTimeUs); 711 e.writeLong(systemTimeUs); 712 pulledData.add(e); 713 }); 714 } 715 716 private void pullKernelUidCpuFreqTime(int tagId, List<StatsLogEventWrapper> pulledData) { 717 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 718 mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { 719 for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { 720 if(cpuFreqTimeMs[freqIndex] != 0) { 721 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3); 722 e.writeInt(uid); 723 e.writeInt(freqIndex); 724 e.writeLong(cpuFreqTimeMs[freqIndex]); 725 pulledData.add(e); 726 } 727 } 728 }); 729 } 730 731 private void pullKernelUidCpuClusterTime(int tagId, List<StatsLogEventWrapper> pulledData) { 732 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 733 mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> { 734 for (int i = 0; i < cpuClusterTimesMs.length; i++) { 735 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3); 736 e.writeInt(uid); 737 e.writeInt(i); 738 e.writeLong(cpuClusterTimesMs[i]); 739 pulledData.add(e); 740 } 741 }); 742 } 743 744 private void pullKernelUidCpuActiveTime(int tagId, List<StatsLogEventWrapper> pulledData) { 745 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 746 mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> { 747 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2); 748 e.writeInt(uid); 749 e.writeLong((long)cpuActiveTimesMs); 750 pulledData.add(e); 751 }); 752 } 753 754 private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) { 755 long token = Binder.clearCallingIdentity(); 756 if (mWifiManager == null) { 757 mWifiManager = 758 IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE)); 759 } 760 if (mWifiManager != null) { 761 try { 762 SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); 763 mWifiManager.requestActivityInfo(wifiReceiver); 764 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); 765 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6); 766 e.writeLong(wifiInfo.getTimeStamp()); 767 e.writeInt(wifiInfo.getStackState()); 768 e.writeLong(wifiInfo.getControllerTxTimeMillis()); 769 e.writeLong(wifiInfo.getControllerRxTimeMillis()); 770 e.writeLong(wifiInfo.getControllerIdleTimeMillis()); 771 e.writeLong(wifiInfo.getControllerEnergyUsed()); 772 pulledData.add(e); 773 } catch (RemoteException e) { 774 Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e); 775 } finally { 776 Binder.restoreCallingIdentity(token); 777 } 778 } 779 } 780 781 private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) { 782 long token = Binder.clearCallingIdentity(); 783 if (mTelephony == null) { 784 mTelephony = TelephonyManager.from(mContext); 785 } 786 if (mTelephony != null) { 787 SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); 788 mTelephony.requestModemActivityInfo(modemReceiver); 789 final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); 790 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6); 791 e.writeLong(modemInfo.getTimestamp()); 792 e.writeLong(modemInfo.getSleepTimeMillis()); 793 e.writeLong(modemInfo.getIdleTimeMillis()); 794 e.writeLong(modemInfo.getTxTimeMillis()[0]); 795 e.writeLong(modemInfo.getTxTimeMillis()[1]); 796 e.writeLong(modemInfo.getTxTimeMillis()[2]); 797 e.writeLong(modemInfo.getTxTimeMillis()[3]); 798 e.writeLong(modemInfo.getTxTimeMillis()[4]); 799 e.writeLong(modemInfo.getRxTimeMillis()); 800 e.writeLong(modemInfo.getEnergyUsed()); 801 pulledData.add(e); 802 } 803 } 804 805 private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) { 806 BluetoothActivityEnergyInfo info = pullBluetoothData(); 807 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6); 808 e.writeLong(info.getTimeStamp()); 809 e.writeInt(info.getBluetoothStackState()); 810 e.writeLong(info.getControllerTxTimeMillis()); 811 e.writeLong(info.getControllerRxTimeMillis()); 812 e.writeLong(info.getControllerIdleTimeMillis()); 813 e.writeLong(info.getControllerEnergyUsed()); 814 pulledData.add(e); 815 } 816 817 private synchronized BluetoothActivityEnergyInfo pullBluetoothData() { 818 final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 819 if (adapter != null) { 820 SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth"); 821 adapter.requestControllerActivityEnergyInfo(bluetoothReceiver); 822 return awaitControllerInfo(bluetoothReceiver); 823 } else { 824 Slog.e(TAG, "Failed to get bluetooth adapter!"); 825 return null; 826 } 827 } 828 829 private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) { 830 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1); 831 e.writeLong(SystemClock.elapsedRealtime()); 832 pulledData.add(e); 833 } 834 835 private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) { 836 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 3); 837 e.writeLong(mStatFsData.getAvailableBytes()); 838 e.writeLong(mStatFsSystem.getAvailableBytes()); 839 e.writeLong(mStatFsTemp.getAvailableBytes()); 840 pulledData.add(e); 841 } 842 843 private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) { 844 StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1); 845 e.writeLong(SystemClock.uptimeMillis()); 846 pulledData.add(e); 847 } 848 849 private void pullProcessMemoryState(int tagId, List<StatsLogEventWrapper> pulledData) { 850 List<ProcessMemoryState> processMemoryStates = 851 LocalServices.getService(ActivityManagerInternal.class) 852 .getMemoryStateForProcesses(); 853 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 854 for (ProcessMemoryState processMemoryState : processMemoryStates) { 855 StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */); 856 e.writeInt(processMemoryState.uid); 857 e.writeString(processMemoryState.processName); 858 e.writeInt(processMemoryState.oomScore); 859 e.writeLong(processMemoryState.pgfault); 860 e.writeLong(processMemoryState.pgmajfault); 861 e.writeLong(processMemoryState.rssInBytes); 862 e.writeLong(processMemoryState.cacheInBytes); 863 e.writeLong(processMemoryState.swapInBytes); 864 pulledData.add(e); 865 } 866 } 867 868 /** 869 * Pulls various data. 870 */ 871 @Override // Binder call 872 public StatsLogEventWrapper[] pullData(int tagId) { 873 enforceCallingPermission(); 874 if (DEBUG) 875 Slog.d(TAG, "Pulling " + tagId); 876 List<StatsLogEventWrapper> ret = new ArrayList<>(); 877 switch (tagId) { 878 case StatsLog.WIFI_BYTES_TRANSFER: { 879 pullWifiBytesTransfer(tagId, ret); 880 break; 881 } 882 case StatsLog.MOBILE_BYTES_TRANSFER: { 883 pullMobileBytesTransfer(tagId, ret); 884 break; 885 } 886 case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: { 887 pullWifiBytesTransferByFgBg(tagId, ret); 888 break; 889 } 890 case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: { 891 pullMobileBytesTransferByFgBg(tagId, ret); 892 break; 893 } 894 case StatsLog.BLUETOOTH_BYTES_TRANSFER: { 895 pullBluetoothBytesTransfer(tagId, ret); 896 break; 897 } 898 case StatsLog.KERNEL_WAKELOCK: { 899 pullKernelWakelock(tagId, ret); 900 break; 901 } 902 case StatsLog.CPU_TIME_PER_FREQ: { 903 pullCpuTimePerFreq(tagId, ret); 904 break; 905 } 906 case StatsLog.CPU_TIME_PER_UID: { 907 pullKernelUidCpuTime(tagId, ret); 908 break; 909 } 910 case StatsLog.CPU_TIME_PER_UID_FREQ: { 911 pullKernelUidCpuFreqTime(tagId, ret); 912 break; 913 } 914 case StatsLog.CPU_CLUSTER_TIME: { 915 pullKernelUidCpuClusterTime(tagId, ret); 916 break; 917 } 918 case StatsLog.CPU_ACTIVE_TIME: { 919 pullKernelUidCpuActiveTime(tagId, ret); 920 break; 921 } 922 case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: { 923 pullWifiActivityEnergyInfo(tagId, ret); 924 break; 925 } 926 case StatsLog.MODEM_ACTIVITY_INFO: { 927 pullModemActivityInfo(tagId, ret); 928 break; 929 } 930 case StatsLog.BLUETOOTH_ACTIVITY_INFO: { 931 pullBluetoothActivityInfo(tagId, ret); 932 break; 933 } 934 case StatsLog.SYSTEM_UPTIME: { 935 pullSystemUpTime(tagId, ret); 936 break; 937 } 938 case StatsLog.SYSTEM_ELAPSED_REALTIME: { 939 pullSystemElapsedRealtime(tagId, ret); 940 break; 941 } 942 case StatsLog.DISK_SPACE: { 943 pullDiskSpace(tagId, ret); 944 break; 945 } 946 case StatsLog.PROCESS_MEMORY_STATE: { 947 pullProcessMemoryState(tagId, ret); 948 break; 949 } 950 default: 951 Slog.w(TAG, "No such tagId data as " + tagId); 952 return null; 953 } 954 return ret.toArray(new StatsLogEventWrapper[ret.size()]); 955 } 956 957 @Override // Binder call 958 public void statsdReady() { 959 enforceCallingPermission(); 960 if (DEBUG) Slog.d(TAG, "learned that statsdReady"); 961 sayHiToStatsd(); // tell statsd that we're ready too and link to it 962 mContext.sendBroadcastAsUser( 963 new Intent(StatsManager.ACTION_STATSD_STARTED) 964 .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND), 965 UserHandle.SYSTEM, 966 android.Manifest.permission.DUMP); 967 } 968 969 @Override 970 public void triggerUidSnapshot() { 971 enforceCallingPermission(); 972 synchronized (sStatsdLock) { 973 final long token = Binder.clearCallingIdentity(); 974 try { 975 informAllUidsLocked(mContext); 976 } catch (RemoteException e) { 977 Slog.e(TAG, "Failed to trigger uid snapshot.", e); 978 } finally { 979 restoreCallingIdentity(token); 980 } 981 } 982 } 983 984 private void enforceCallingPermission() { 985 if (Binder.getCallingPid() == Process.myPid()) { 986 return; 987 } 988 mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null); 989 } 990 991 // Lifecycle and related code 992 993 /** 994 * Fetches the statsd IBinder service 995 */ 996 private static IStatsManager fetchStatsdService() { 997 return IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); 998 } 999 1000 public static final class Lifecycle extends SystemService { 1001 private StatsCompanionService mStatsCompanionService; 1002 1003 public Lifecycle(Context context) { 1004 super(context); 1005 } 1006 1007 @Override 1008 public void onStart() { 1009 mStatsCompanionService = new StatsCompanionService(getContext()); 1010 try { 1011 publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService); 1012 if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE); 1013 } catch (Exception e) { 1014 Slog.e(TAG, "Failed to publishBinderService", e); 1015 } 1016 } 1017 1018 @Override 1019 public void onBootPhase(int phase) { 1020 super.onBootPhase(phase); 1021 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 1022 mStatsCompanionService.systemReady(); 1023 } 1024 } 1025 } 1026 1027 /** 1028 * Now that the android system is ready, StatsCompanion is ready too, so inform statsd. 1029 */ 1030 private void systemReady() { 1031 if (DEBUG) Slog.d(TAG, "Learned that systemReady"); 1032 sayHiToStatsd(); 1033 } 1034 1035 /** 1036 * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. 1037 */ 1038 private void sayHiToStatsd() { 1039 synchronized (sStatsdLock) { 1040 if (sStatsd != null) { 1041 Slog.e(TAG, "Trying to fetch statsd, but it was already fetched", 1042 new IllegalStateException("sStatsd is not null when being fetched")); 1043 return; 1044 } 1045 sStatsd = fetchStatsdService(); 1046 if (sStatsd == null) { 1047 Slog.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive."); 1048 return; 1049 } 1050 if (DEBUG) Slog.d(TAG, "Saying hi to statsd"); 1051 try { 1052 sStatsd.statsCompanionReady(); 1053 // If the statsCompanionReady two-way binder call returns, link to statsd. 1054 try { 1055 sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); 1056 } catch (RemoteException e) { 1057 Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e); 1058 forgetEverything(); 1059 } 1060 // Setup broadcast receiver for updates. 1061 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED); 1062 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 1063 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1064 filter.addDataScheme("package"); 1065 mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null, 1066 null); 1067 1068 // Setup receiver for user initialize (which happens once for a new user) and 1069 // if a user is removed. 1070 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE); 1071 filter.addAction(Intent.ACTION_USER_REMOVED); 1072 mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL, 1073 filter, null, null); 1074 1075 // Setup receiver for device reboots or shutdowns. 1076 filter = new IntentFilter(Intent.ACTION_REBOOT); 1077 filter.addAction(Intent.ACTION_SHUTDOWN); 1078 mContext.registerReceiverAsUser( 1079 mShutdownEventReceiver, UserHandle.ALL, filter, null, null); 1080 final long token = Binder.clearCallingIdentity(); 1081 try { 1082 // Pull the latest state of UID->app name, version mapping when statsd starts. 1083 informAllUidsLocked(mContext); 1084 } finally { 1085 restoreCallingIdentity(token); 1086 } 1087 Slog.i(TAG, "Told statsd that StatsCompanionService is alive."); 1088 } catch (RemoteException e) { 1089 Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e); 1090 forgetEverything(); 1091 } 1092 } 1093 } 1094 1095 private class StatsdDeathRecipient implements IBinder.DeathRecipient { 1096 @Override 1097 public void binderDied() { 1098 Slog.i(TAG, "Statsd is dead - erase all my knowledge."); 1099 forgetEverything(); 1100 } 1101 } 1102 1103 private void forgetEverything() { 1104 synchronized (sStatsdLock) { 1105 sStatsd = null; 1106 mContext.unregisterReceiver(mAppUpdateReceiver); 1107 mContext.unregisterReceiver(mUserUpdateReceiver); 1108 mContext.unregisterReceiver(mShutdownEventReceiver); 1109 cancelAnomalyAlarm(); 1110 cancelPullingAlarm(); 1111 } 1112 } 1113 1114} 1115