1 /* 2 * Copyright (C) 2019 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 */ 16 17 package com.android.server.stats; 18 19 import static com.android.server.stats.StatsCompanion.PendingIntentRef; 20 21 import android.Manifest; 22 import android.annotation.Nullable; 23 import android.app.AppOpsManager; 24 import android.app.PendingIntent; 25 import android.content.Context; 26 import android.os.Binder; 27 import android.os.IPullAtomCallback; 28 import android.os.IStatsManagerService; 29 import android.os.IStatsQueryCallback; 30 import android.os.IStatsd; 31 import android.os.PowerManager; 32 import android.os.Process; 33 import android.os.RemoteException; 34 import android.util.ArrayMap; 35 import android.util.Log; 36 37 import com.android.internal.annotations.GuardedBy; 38 39 import java.util.Map; 40 import java.util.Objects; 41 42 /** 43 * Service for {@link android.app.StatsManager}. 44 * 45 * @hide 46 */ 47 public class StatsManagerService extends IStatsManagerService.Stub { 48 49 private static final String TAG = "StatsManagerService"; 50 private static final boolean DEBUG = false; 51 52 private static final int STATSD_TIMEOUT_MILLIS = 5000; 53 54 private static final String USAGE_STATS_PERMISSION_OPS = "android:get_usage_stats"; 55 56 @GuardedBy("mLock") 57 private IStatsd mStatsd; 58 private final Object mLock = new Object(); 59 60 private StatsCompanionService mStatsCompanionService; 61 private Context mContext; 62 63 @GuardedBy("mLock") 64 private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>(); 65 @GuardedBy("mLock") 66 private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap = new ArrayMap<>(); 67 @GuardedBy("mLock") 68 private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap = 69 new ArrayMap<>(); 70 @GuardedBy("mLock") 71 private ArrayMap<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> 72 mRestrictedMetricsPirMap = new ArrayMap<>(); 73 StatsManagerService(Context context)74 public StatsManagerService(Context context) { 75 super(); 76 mContext = context; 77 } 78 79 private static class ConfigKey { 80 private final int mUid; 81 private final long mConfigId; 82 ConfigKey(int uid, long configId)83 ConfigKey(int uid, long configId) { 84 mUid = uid; 85 mConfigId = configId; 86 } 87 getUid()88 public int getUid() { 89 return mUid; 90 } 91 getConfigId()92 public long getConfigId() { 93 return mConfigId; 94 } 95 96 @Override hashCode()97 public int hashCode() { 98 return Objects.hash(mUid, mConfigId); 99 } 100 101 @Override equals(Object obj)102 public boolean equals(Object obj) { 103 if (obj instanceof ConfigKey) { 104 ConfigKey other = (ConfigKey) obj; 105 return this.mUid == other.getUid() && this.mConfigId == other.getConfigId(); 106 } 107 return false; 108 } 109 } 110 111 private static class ConfigKeyWithPackage { 112 private final String mConfigPackage; 113 private final long mConfigId; 114 ConfigKeyWithPackage(String configPackage, long configId)115 ConfigKeyWithPackage(String configPackage, long configId) { 116 mConfigPackage = configPackage; 117 mConfigId = configId; 118 } 119 getConfigPackage()120 public String getConfigPackage() { 121 return mConfigPackage; 122 } 123 getConfigId()124 public long getConfigId() { 125 return mConfigId; 126 } 127 128 @Override hashCode()129 public int hashCode() { 130 return Objects.hash(mConfigPackage, mConfigId); 131 } 132 133 @Override equals(Object obj)134 public boolean equals(Object obj) { 135 if (obj instanceof ConfigKeyWithPackage) { 136 ConfigKeyWithPackage other = (ConfigKeyWithPackage) obj; 137 return this.mConfigPackage.equals(other.getConfigPackage()) 138 && this.mConfigId == other.getConfigId(); 139 } 140 return false; 141 } 142 } 143 144 private static class PullerKey { 145 private final int mUid; 146 private final int mAtomTag; 147 PullerKey(int uid, int atom)148 PullerKey(int uid, int atom) { 149 mUid = uid; 150 mAtomTag = atom; 151 } 152 getUid()153 public int getUid() { 154 return mUid; 155 } 156 getAtom()157 public int getAtom() { 158 return mAtomTag; 159 } 160 161 @Override hashCode()162 public int hashCode() { 163 return Objects.hash(mUid, mAtomTag); 164 } 165 166 @Override equals(Object obj)167 public boolean equals(Object obj) { 168 if (obj instanceof PullerKey) { 169 PullerKey other = (PullerKey) obj; 170 return this.mUid == other.getUid() && this.mAtomTag == other.getAtom(); 171 } 172 return false; 173 } 174 } 175 176 private static class PullerValue { 177 private final long mCoolDownMillis; 178 private final long mTimeoutMillis; 179 private final int[] mAdditiveFields; 180 private final IPullAtomCallback mCallback; 181 PullerValue(long coolDownMillis, long timeoutMillis, int[] additiveFields, IPullAtomCallback callback)182 PullerValue(long coolDownMillis, long timeoutMillis, int[] additiveFields, 183 IPullAtomCallback callback) { 184 mCoolDownMillis = coolDownMillis; 185 mTimeoutMillis = timeoutMillis; 186 mAdditiveFields = additiveFields; 187 mCallback = callback; 188 } 189 getCoolDownMillis()190 public long getCoolDownMillis() { 191 return mCoolDownMillis; 192 } 193 getTimeoutMillis()194 public long getTimeoutMillis() { 195 return mTimeoutMillis; 196 } 197 getAdditiveFields()198 public int[] getAdditiveFields() { 199 return mAdditiveFields; 200 } 201 getCallback()202 public IPullAtomCallback getCallback() { 203 return mCallback; 204 } 205 } 206 207 private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>(); 208 209 @Override registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis, int[] additiveFields, IPullAtomCallback pullerCallback)210 public void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis, 211 int[] additiveFields, IPullAtomCallback pullerCallback) { 212 enforceRegisterStatsPullAtomPermission(); 213 if (pullerCallback == null) { 214 Log.w(TAG, "Puller callback is null for atom " + atomTag); 215 return; 216 } 217 int callingUid = Binder.getCallingUid(); 218 PullerKey key = new PullerKey(callingUid, atomTag); 219 PullerValue val = 220 new PullerValue(coolDownMillis, timeoutMillis, additiveFields, pullerCallback); 221 222 // Always cache the puller in StatsManagerService. If statsd is down, we will register the 223 // puller when statsd comes back up. 224 synchronized (mLock) { 225 mPullers.put(key, val); 226 } 227 228 IStatsd statsd = getStatsdNonblocking(); 229 if (statsd == null) { 230 return; 231 } 232 233 final long token = Binder.clearCallingIdentity(); 234 try { 235 statsd.registerPullAtomCallback(callingUid, atomTag, coolDownMillis, timeoutMillis, 236 additiveFields, pullerCallback); 237 } catch (RemoteException e) { 238 Log.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); 239 } finally { 240 Binder.restoreCallingIdentity(token); 241 } 242 } 243 244 @Override unregisterPullAtomCallback(int atomTag)245 public void unregisterPullAtomCallback(int atomTag) { 246 enforceRegisterStatsPullAtomPermission(); 247 int callingUid = Binder.getCallingUid(); 248 PullerKey key = new PullerKey(callingUid, atomTag); 249 250 // Always remove the puller from StatsManagerService even if statsd is down. When statsd 251 // comes back up, we will not re-register the removed puller. 252 synchronized (mLock) { 253 mPullers.remove(key); 254 } 255 256 IStatsd statsd = getStatsdNonblocking(); 257 if (statsd == null) { 258 return; 259 } 260 261 final long token = Binder.clearCallingIdentity(); 262 try { 263 statsd.unregisterPullAtomCallback(callingUid, atomTag); 264 } catch (RemoteException e) { 265 Log.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag); 266 } finally { 267 Binder.restoreCallingIdentity(token); 268 } 269 } 270 271 @Override setDataFetchOperation(long configId, PendingIntent pendingIntent, String packageName)272 public void setDataFetchOperation(long configId, PendingIntent pendingIntent, 273 String packageName) { 274 enforceDumpAndUsageStatsPermission(packageName); 275 int callingUid = Binder.getCallingUid(); 276 final long token = Binder.clearCallingIdentity(); 277 PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext); 278 ConfigKey key = new ConfigKey(callingUid, configId); 279 // We add the PIR to a map so we can reregister if statsd is unavailable. 280 synchronized (mLock) { 281 mDataFetchPirMap.put(key, pir); 282 } 283 try { 284 IStatsd statsd = getStatsdNonblocking(); 285 if (statsd != null) { 286 statsd.setDataFetchOperation(configId, pir, callingUid); 287 } 288 } catch (RemoteException e) { 289 Log.e(TAG, "Failed to setDataFetchOperation with statsd"); 290 } finally { 291 Binder.restoreCallingIdentity(token); 292 } 293 } 294 295 @Override removeDataFetchOperation(long configId, String packageName)296 public void removeDataFetchOperation(long configId, String packageName) { 297 enforceDumpAndUsageStatsPermission(packageName); 298 int callingUid = Binder.getCallingUid(); 299 final long token = Binder.clearCallingIdentity(); 300 ConfigKey key = new ConfigKey(callingUid, configId); 301 synchronized (mLock) { 302 mDataFetchPirMap.remove(key); 303 } 304 try { 305 IStatsd statsd = getStatsdNonblocking(); 306 if (statsd != null) { 307 statsd.removeDataFetchOperation(configId, callingUid); 308 } 309 } catch (RemoteException e) { 310 Log.e(TAG, "Failed to removeDataFetchOperation with statsd"); 311 } finally { 312 Binder.restoreCallingIdentity(token); 313 } 314 } 315 316 @Override setActiveConfigsChangedOperation(PendingIntent pendingIntent, String packageName)317 public long[] setActiveConfigsChangedOperation(PendingIntent pendingIntent, 318 String packageName) { 319 enforceDumpAndUsageStatsPermission(packageName); 320 int callingUid = Binder.getCallingUid(); 321 final long token = Binder.clearCallingIdentity(); 322 PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext); 323 // We add the PIR to a map so we can reregister if statsd is unavailable. 324 synchronized (mLock) { 325 mActiveConfigsPirMap.put(callingUid, pir); 326 } 327 try { 328 IStatsd statsd = getStatsdNonblocking(); 329 if (statsd != null) { 330 return statsd.setActiveConfigsChangedOperation(pir, callingUid); 331 } 332 } catch (RemoteException e) { 333 Log.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd"); 334 } finally { 335 Binder.restoreCallingIdentity(token); 336 } 337 return new long[] {}; 338 } 339 340 @Override removeActiveConfigsChangedOperation(String packageName)341 public void removeActiveConfigsChangedOperation(String packageName) { 342 enforceDumpAndUsageStatsPermission(packageName); 343 int callingUid = Binder.getCallingUid(); 344 final long token = Binder.clearCallingIdentity(); 345 synchronized (mLock) { 346 mActiveConfigsPirMap.remove(callingUid); 347 } 348 try { 349 IStatsd statsd = getStatsdNonblocking(); 350 if (statsd != null) { 351 statsd.removeActiveConfigsChangedOperation(callingUid); 352 } 353 } catch (RemoteException e) { 354 Log.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd"); 355 } finally { 356 Binder.restoreCallingIdentity(token); 357 } 358 } 359 360 @Override setBroadcastSubscriber(long configId, long subscriberId, PendingIntent pendingIntent, String packageName)361 public void setBroadcastSubscriber(long configId, long subscriberId, 362 PendingIntent pendingIntent, String packageName) { 363 enforceDumpAndUsageStatsPermission(packageName); 364 int callingUid = Binder.getCallingUid(); 365 final long token = Binder.clearCallingIdentity(); 366 PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext); 367 ConfigKey key = new ConfigKey(callingUid, configId); 368 // We add the PIR to a map so we can reregister if statsd is unavailable. 369 synchronized (mLock) { 370 ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap 371 .getOrDefault(key, new ArrayMap<>()); 372 innerMap.put(subscriberId, pir); 373 mBroadcastSubscriberPirMap.put(key, innerMap); 374 } 375 try { 376 IStatsd statsd = getStatsdNonblocking(); 377 if (statsd != null) { 378 statsd.setBroadcastSubscriber( 379 configId, subscriberId, pir, callingUid); 380 } 381 } catch (RemoteException e) { 382 Log.e(TAG, "Failed to setBroadcastSubscriber with statsd"); 383 } finally { 384 Binder.restoreCallingIdentity(token); 385 } 386 } 387 388 @Override unsetBroadcastSubscriber(long configId, long subscriberId, String packageName)389 public void unsetBroadcastSubscriber(long configId, long subscriberId, String packageName) { 390 enforceDumpAndUsageStatsPermission(packageName); 391 int callingUid = Binder.getCallingUid(); 392 final long token = Binder.clearCallingIdentity(); 393 ConfigKey key = new ConfigKey(callingUid, configId); 394 synchronized (mLock) { 395 ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap 396 .getOrDefault(key, new ArrayMap<>()); 397 innerMap.remove(subscriberId); 398 if (innerMap.isEmpty()) { 399 mBroadcastSubscriberPirMap.remove(key); 400 } 401 } 402 try { 403 IStatsd statsd = getStatsdNonblocking(); 404 if (statsd != null) { 405 statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid); 406 } 407 } catch (RemoteException e) { 408 Log.e(TAG, "Failed to unsetBroadcastSubscriber with statsd"); 409 } finally { 410 Binder.restoreCallingIdentity(token); 411 } 412 } 413 414 @Override getRegisteredExperimentIds()415 public long[] getRegisteredExperimentIds() throws IllegalStateException { 416 enforceDumpAndUsageStatsPermission(null); 417 final long token = Binder.clearCallingIdentity(); 418 try { 419 IStatsd statsd = waitForStatsd(); 420 if (statsd != null) { 421 return statsd.getRegisteredExperimentIds(); 422 } 423 } catch (RemoteException e) { 424 Log.e(TAG, "Failed to getRegisteredExperimentIds with statsd"); 425 throw new IllegalStateException(e.getMessage(), e); 426 } finally { 427 Binder.restoreCallingIdentity(token); 428 } 429 throw new IllegalStateException("Failed to connect to statsd to registerExperimentIds"); 430 } 431 432 @Override getMetadata(String packageName)433 public byte[] getMetadata(String packageName) throws IllegalStateException { 434 enforceDumpAndUsageStatsPermission(packageName); 435 final long token = Binder.clearCallingIdentity(); 436 try { 437 IStatsd statsd = waitForStatsd(); 438 if (statsd != null) { 439 return statsd.getMetadata(); 440 } 441 } catch (RemoteException e) { 442 Log.e(TAG, "Failed to getMetadata with statsd"); 443 throw new IllegalStateException(e.getMessage(), e); 444 } finally { 445 Binder.restoreCallingIdentity(token); 446 } 447 throw new IllegalStateException("Failed to connect to statsd to getMetadata"); 448 } 449 450 @Override getData(long key, String packageName)451 public byte[] getData(long key, String packageName) throws IllegalStateException { 452 enforceDumpAndUsageStatsPermission(packageName); 453 PowerManager powerManager = (PowerManager) 454 mContext.getSystemService(Context.POWER_SERVICE); 455 PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 456 /*tag=*/ StatsManagerService.class.getCanonicalName()); 457 int callingUid = Binder.getCallingUid(); 458 final long token = Binder.clearCallingIdentity(); 459 wl.acquire(); 460 try { 461 IStatsd statsd = waitForStatsd(); 462 if (statsd != null) { 463 return statsd.getData(key, callingUid); 464 } 465 } catch (RemoteException e) { 466 Log.e(TAG, "Failed to getData with statsd"); 467 throw new IllegalStateException(e.getMessage(), e); 468 } finally { 469 wl.release(); 470 Binder.restoreCallingIdentity(token); 471 } 472 throw new IllegalStateException("Failed to connect to statsd to getData"); 473 } 474 475 @Override addConfiguration(long configId, byte[] config, String packageName)476 public void addConfiguration(long configId, byte[] config, String packageName) 477 throws IllegalStateException { 478 enforceDumpAndUsageStatsPermission(packageName); 479 int callingUid = Binder.getCallingUid(); 480 final long token = Binder.clearCallingIdentity(); 481 try { 482 IStatsd statsd = waitForStatsd(); 483 if (statsd != null) { 484 statsd.addConfiguration(configId, config, callingUid); 485 return; 486 } 487 } catch (RemoteException e) { 488 Log.e(TAG, "Failed to addConfiguration with statsd"); 489 throw new IllegalStateException(e.getMessage(), e); 490 } finally { 491 Binder.restoreCallingIdentity(token); 492 } 493 throw new IllegalStateException("Failed to connect to statsd to addConfig"); 494 } 495 496 @Override removeConfiguration(long configId, String packageName)497 public void removeConfiguration(long configId, String packageName) 498 throws IllegalStateException { 499 enforceDumpAndUsageStatsPermission(packageName); 500 int callingUid = Binder.getCallingUid(); 501 final long token = Binder.clearCallingIdentity(); 502 try { 503 IStatsd statsd = waitForStatsd(); 504 if (statsd != null) { 505 statsd.removeConfiguration(configId, callingUid); 506 return; 507 } 508 } catch (RemoteException e) { 509 Log.e(TAG, "Failed to removeConfiguration with statsd"); 510 throw new IllegalStateException(e.getMessage(), e); 511 } finally { 512 Binder.restoreCallingIdentity(token); 513 } 514 throw new IllegalStateException("Failed to connect to statsd to removeConfig"); 515 } 516 517 @Override setRestrictedMetricsChangedOperation(PendingIntent pendingIntent, long configId, String configPackage)518 public long[] setRestrictedMetricsChangedOperation(PendingIntent pendingIntent, 519 long configId, String configPackage) { 520 enforceRestrictedStatsPermission(); 521 int callingUid = Binder.getCallingUid(); 522 final long token = Binder.clearCallingIdentity(); 523 PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext); 524 ConfigKeyWithPackage key = new ConfigKeyWithPackage(configPackage, configId); 525 // Add the PIR to a map so we can re-register if statsd is unavailable. 526 synchronized (mLock) { 527 ArrayMap<Integer, PendingIntentRef> innerMap = mRestrictedMetricsPirMap.getOrDefault( 528 key, new ArrayMap<>()); 529 innerMap.put(callingUid, pir); 530 mRestrictedMetricsPirMap.put(key, innerMap); 531 } 532 try { 533 IStatsd statsd = getStatsdNonblocking(); 534 if (statsd != null) { 535 return statsd.setRestrictedMetricsChangedOperation(configId, configPackage, pir, 536 callingUid); 537 } 538 } catch (RemoteException e) { 539 Log.e(TAG, "Failed to setRestrictedMetricsChangedOperation with statsd"); 540 } finally { 541 Binder.restoreCallingIdentity(token); 542 } 543 return new long[]{}; 544 } 545 546 @Override removeRestrictedMetricsChangedOperation(long configId, String configPackage)547 public void removeRestrictedMetricsChangedOperation(long configId, String configPackage) { 548 enforceRestrictedStatsPermission(); 549 int callingUid = Binder.getCallingUid(); 550 final long token = Binder.clearCallingIdentity(); 551 ConfigKeyWithPackage key = new ConfigKeyWithPackage(configPackage, configId); 552 synchronized (mLock) { 553 ArrayMap<Integer, PendingIntentRef> innerMap = mRestrictedMetricsPirMap.getOrDefault( 554 key, new ArrayMap<>()); 555 innerMap.remove(callingUid); 556 if (innerMap.isEmpty()) { 557 mRestrictedMetricsPirMap.remove(key); 558 } 559 } 560 try { 561 IStatsd statsd = getStatsdNonblocking(); 562 if (statsd != null) { 563 statsd.removeRestrictedMetricsChangedOperation(configId, configPackage, callingUid); 564 } 565 } catch (RemoteException e) { 566 Log.e(TAG, "Failed to removeRestrictedMetricsChangedOperation with statsd"); 567 } finally { 568 Binder.restoreCallingIdentity(token); 569 } 570 } 571 572 @Override querySql(String sqlQuery, int minSqlClientVersion, byte[] policyConfig, IStatsQueryCallback queryCallback, long configKey, String configPackage)573 public void querySql(String sqlQuery, int minSqlClientVersion, byte[] policyConfig, 574 IStatsQueryCallback queryCallback, long configKey, String configPackage) { 575 int callingUid = Binder.getCallingUid(); 576 enforceRestrictedStatsPermission(); 577 final long token = Binder.clearCallingIdentity(); 578 try { 579 IStatsd statsd = waitForStatsd(); 580 if (statsd != null) { 581 statsd.querySql( 582 sqlQuery, 583 minSqlClientVersion, 584 policyConfig, 585 queryCallback, 586 configKey, 587 configPackage, 588 callingUid); 589 } else { 590 queryCallback.sendFailure("Could not connect to statsd from system server"); 591 } 592 } catch (RemoteException e) { 593 throw new IllegalStateException(e.getMessage(), e); 594 } finally { 595 Binder.restoreCallingIdentity(token); 596 } 597 } 598 setStatsCompanionService(StatsCompanionService statsCompanionService)599 void setStatsCompanionService(StatsCompanionService statsCompanionService) { 600 mStatsCompanionService = statsCompanionService; 601 } 602 603 /** Checks that the caller has READ_RESTRICTED_STATS permission. */ enforceRestrictedStatsPermission()604 private void enforceRestrictedStatsPermission() { 605 mContext.enforceCallingPermission(Manifest.permission.READ_RESTRICTED_STATS, null); 606 } 607 608 /** 609 * Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that 610 * the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null. 611 * 612 * @param packageName The packageName to check USAGE_STATS_PERMISSION_OPS. 613 */ enforceDumpAndUsageStatsPermission(@ullable String packageName)614 private void enforceDumpAndUsageStatsPermission(@Nullable String packageName) { 615 int callingUid = Binder.getCallingUid(); 616 int callingPid = Binder.getCallingPid(); 617 618 if (callingPid == Process.myPid()) { 619 return; 620 } 621 622 mContext.enforceCallingPermission(Manifest.permission.DUMP, null); 623 mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null); 624 625 if (packageName == null) { 626 return; 627 } 628 AppOpsManager appOpsManager = (AppOpsManager) mContext 629 .getSystemService(Context.APP_OPS_SERVICE); 630 switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS, 631 Binder.getCallingUid(), packageName, null, null)) { 632 case AppOpsManager.MODE_ALLOWED: 633 case AppOpsManager.MODE_DEFAULT: 634 break; 635 default: 636 throw new SecurityException( 637 String.format("UID %d / PID %d lacks app-op %s", 638 callingUid, callingPid, USAGE_STATS_PERMISSION_OPS) 639 ); 640 } 641 } 642 enforceRegisterStatsPullAtomPermission()643 private void enforceRegisterStatsPullAtomPermission() { 644 mContext.enforceCallingOrSelfPermission( 645 android.Manifest.permission.REGISTER_STATS_PULL_ATOM, 646 "Need REGISTER_STATS_PULL_ATOM permission."); 647 } 648 649 650 /** 651 * Clients should call this if blocking until statsd to be ready is desired 652 * 653 * @return IStatsd object if statsd becomes ready within the timeout, null otherwise. 654 */ waitForStatsd()655 private IStatsd waitForStatsd() { 656 synchronized (mLock) { 657 if (mStatsd == null) { 658 try { 659 mLock.wait(STATSD_TIMEOUT_MILLIS); 660 } catch (InterruptedException e) { 661 Log.e(TAG, "wait for statsd interrupted"); 662 } 663 } 664 return mStatsd; 665 } 666 } 667 668 /** 669 * Clients should call this to receive a reference to statsd. 670 * 671 * @return IStatsd object if statsd is ready, null otherwise. 672 */ getStatsdNonblocking()673 private IStatsd getStatsdNonblocking() { 674 synchronized (mLock) { 675 return mStatsd; 676 } 677 } 678 679 /** 680 * Called from {@link StatsCompanionService}. 681 * 682 * Tells StatsManagerService that Statsd is ready and updates 683 * Statsd with the contents of our local cache. 684 */ statsdReady(IStatsd statsd)685 void statsdReady(IStatsd statsd) { 686 synchronized (mLock) { 687 mStatsd = statsd; 688 mLock.notify(); 689 } 690 sayHiToStatsd(statsd); 691 } 692 693 /** 694 * Called from {@link StatsCompanionService}. 695 * 696 * Tells StatsManagerService that Statsd is no longer ready 697 * and we should no longer make binder calls with statsd. 698 */ statsdNotReady()699 void statsdNotReady() { 700 synchronized (mLock) { 701 mStatsd = null; 702 } 703 } 704 sayHiToStatsd(IStatsd statsd)705 private void sayHiToStatsd(IStatsd statsd) { 706 if (statsd == null) { 707 return; 708 } 709 710 final long token = Binder.clearCallingIdentity(); 711 try { 712 registerAllPullers(statsd); 713 registerAllDataFetchOperations(statsd); 714 registerAllActiveConfigsChangedOperations(statsd); 715 registerAllBroadcastSubscribers(statsd); 716 registerAllRestrictedMetricsChangedOperations(statsd); 717 // TODO (b/269419485): register all restricted metric operations. 718 } catch (RemoteException e) { 719 Log.e(TAG, "StatsManager failed to (re-)register data with statsd"); 720 } finally { 721 Binder.restoreCallingIdentity(token); 722 } 723 } 724 725 // Pre-condition: the Binder calling identity has already been cleared registerAllPullers(IStatsd statsd)726 private void registerAllPullers(IStatsd statsd) throws RemoteException { 727 // Since we do not want to make an IPC with the lock held, we first create a copy of the 728 // data with the lock held before iterating through the map. 729 ArrayMap<PullerKey, PullerValue> pullersCopy; 730 synchronized (mLock) { 731 pullersCopy = new ArrayMap<>(mPullers); 732 } 733 734 for (Map.Entry<PullerKey, PullerValue> entry : pullersCopy.entrySet()) { 735 PullerKey key = entry.getKey(); 736 PullerValue value = entry.getValue(); 737 statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownMillis(), 738 value.getTimeoutMillis(), value.getAdditiveFields(), value.getCallback()); 739 } 740 statsd.allPullersFromBootRegistered(); 741 } 742 743 // Pre-condition: the Binder calling identity has already been cleared registerAllDataFetchOperations(IStatsd statsd)744 private void registerAllDataFetchOperations(IStatsd statsd) throws RemoteException { 745 // Since we do not want to make an IPC with the lock held, we first create a copy of the 746 // data with the lock held before iterating through the map. 747 ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy; 748 synchronized (mLock) { 749 dataFetchCopy = new ArrayMap<>(mDataFetchPirMap); 750 } 751 752 for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) { 753 ConfigKey key = entry.getKey(); 754 statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid()); 755 } 756 } 757 758 // Pre-condition: the Binder calling identity has already been cleared registerAllActiveConfigsChangedOperations(IStatsd statsd)759 private void registerAllActiveConfigsChangedOperations(IStatsd statsd) throws RemoteException { 760 // Since we do not want to make an IPC with the lock held, we first create a copy of the 761 // data with the lock held before iterating through the map. 762 ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy; 763 synchronized (mLock) { 764 activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap); 765 } 766 767 for (Map.Entry<Integer, PendingIntentRef> entry : activeConfigsChangedCopy.entrySet()) { 768 statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey()); 769 } 770 } 771 772 // Pre-condition: the Binder calling identity has already been cleared registerAllBroadcastSubscribers(IStatsd statsd)773 private void registerAllBroadcastSubscribers(IStatsd statsd) throws RemoteException { 774 // Since we do not want to make an IPC with the lock held, we first create a deep copy of 775 // the data with the lock held before iterating through the map. 776 ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy = 777 new ArrayMap<>(); 778 synchronized (mLock) { 779 for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry : 780 mBroadcastSubscriberPirMap.entrySet()) { 781 broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap(entry.getValue())); 782 } 783 } 784 785 for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry : 786 broadcastSubscriberCopy.entrySet()) { 787 ConfigKey configKey = entry.getKey(); 788 for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) { 789 statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(), 790 subscriberEntry.getValue(), configKey.getUid()); 791 } 792 } 793 } 794 795 // Pre-condition: the Binder calling identity has already been cleared registerAllRestrictedMetricsChangedOperations(IStatsd statsd)796 private void registerAllRestrictedMetricsChangedOperations(IStatsd statsd) 797 throws RemoteException { 798 // Since we do not want to make an IPC with the lock held, we first create a deep copy of 799 // the data with the lock held before iterating through the map. 800 ArrayMap<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> restrictedMetricsCopy = 801 new ArrayMap<>(); 802 synchronized (mLock) { 803 for (Map.Entry<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> entry : 804 mRestrictedMetricsPirMap.entrySet()) { 805 restrictedMetricsCopy.put(entry.getKey(), new ArrayMap(entry.getValue())); 806 } 807 } 808 809 for (Map.Entry<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> entry : 810 restrictedMetricsCopy.entrySet()) { 811 ConfigKeyWithPackage configKey = entry.getKey(); 812 for (Map.Entry<Integer, PendingIntentRef> uidEntry : entry.getValue().entrySet()) { 813 statsd.setRestrictedMetricsChangedOperation(configKey.getConfigId(), 814 configKey.getConfigPackage(), uidEntry.getValue(), uidEntry.getKey()); 815 } 816 } 817 } 818 } 819