Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2018 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;
     18 
     19 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
     20 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
     21 
     22 import android.content.Context;
     23 import android.content.pm.PackageInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.database.ContentObserver;
     27 import android.net.Uri;
     28 import android.os.Binder;
     29 import android.os.Process;
     30 import android.os.SystemProperties;
     31 import android.os.UserHandle;
     32 import android.provider.Settings;
     33 import android.util.ArrayMap;
     34 import android.util.ArraySet;
     35 import android.util.KeyValueListParser;
     36 import android.util.Slog;
     37 
     38 import com.android.internal.os.AppIdToPackageMap;
     39 import com.android.internal.os.BackgroundThread;
     40 import com.android.internal.os.BinderCallsStats;
     41 import com.android.internal.os.BinderInternal;
     42 import com.android.internal.os.CachedDeviceState;
     43 
     44 import java.io.FileDescriptor;
     45 import java.io.PrintWriter;
     46 import java.util.ArrayList;
     47 import java.util.List;
     48 
     49 public class BinderCallsStatsService extends Binder {
     50 
     51     private static final String TAG = "BinderCallsStatsService";
     52 
     53     private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
     54             = "persist.sys.binder_calls_detailed_tracking";
     55 
     56     /** Resolves the work source of an incoming binder transaction. */
     57     static class AuthorizedWorkSourceProvider implements BinderInternal.WorkSourceProvider {
     58         private ArraySet<Integer> mAppIdWhitelist;
     59 
     60         AuthorizedWorkSourceProvider() {
     61             mAppIdWhitelist = new ArraySet<>();
     62         }
     63 
     64         public int resolveWorkSourceUid(int untrustedWorkSourceUid) {
     65             final int callingUid = getCallingUid();
     66             final int appId = UserHandle.getAppId(callingUid);
     67             if (mAppIdWhitelist.contains(appId)) {
     68                 final int workSource = untrustedWorkSourceUid;
     69                 final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE;
     70                 return isWorkSourceSet ?  workSource : callingUid;
     71             }
     72             return callingUid;
     73         }
     74 
     75         public void systemReady(Context context) {
     76             mAppIdWhitelist = createAppidWhitelist(context);
     77         }
     78 
     79         public void dump(PrintWriter pw, AppIdToPackageMap packageMap) {
     80             pw.println("AppIds of apps that can set the work source:");
     81             final ArraySet<Integer> whitelist = mAppIdWhitelist;
     82             for (Integer appId : whitelist) {
     83                 pw.println("\t- " + packageMap.mapAppId(appId));
     84             }
     85         }
     86 
     87         protected int getCallingUid() {
     88             return Binder.getCallingUid();
     89         }
     90 
     91         private ArraySet<Integer> createAppidWhitelist(Context context) {
     92             // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access.
     93             final ArraySet<Integer> whitelist = new ArraySet<>();
     94 
     95             // We trust our own process.
     96             whitelist.add(UserHandle.getAppId(Process.myUid()));
     97             // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission.
     98             final PackageManager pm = context.getPackageManager();
     99             final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS };
    100             final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
    101             final List<PackageInfo> packages =
    102                     pm.getPackagesHoldingPermissions(permissions, queryFlags);
    103             final int packagesSize = packages.size();
    104             for (int i = 0; i < packagesSize; i++) {
    105                 final PackageInfo pkgInfo = packages.get(i);
    106                 try {
    107                     final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags);
    108                     final int appId = UserHandle.getAppId(uid);
    109                     whitelist.add(appId);
    110                 } catch (NameNotFoundException e) {
    111                     Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e);
    112                 }
    113             }
    114             return whitelist;
    115         }
    116     }
    117 
    118     /** Listens for flag changes. */
    119     private static class SettingsObserver extends ContentObserver {
    120         private static final String SETTINGS_ENABLED_KEY = "enabled";
    121         private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
    122         private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
    123         private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
    124         private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
    125         private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
    126         private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
    127 
    128         private boolean mEnabled;
    129         private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
    130         private final Context mContext;
    131         private final KeyValueListParser mParser = new KeyValueListParser(',');
    132         private final BinderCallsStats mBinderCallsStats;
    133         private final AuthorizedWorkSourceProvider mWorkSourceProvider;
    134 
    135         SettingsObserver(Context context, BinderCallsStats binderCallsStats,
    136                 AuthorizedWorkSourceProvider workSourceProvider) {
    137             super(BackgroundThread.getHandler());
    138             mContext = context;
    139             context.getContentResolver().registerContentObserver(mUri, false, this,
    140                     UserHandle.USER_SYSTEM);
    141             mBinderCallsStats = binderCallsStats;
    142             mWorkSourceProvider = workSourceProvider;
    143             // Always kick once to ensure that we match current state
    144             onChange();
    145         }
    146 
    147         @Override
    148         public void onChange(boolean selfChange, Uri uri, int userId) {
    149             if (mUri.equals(uri)) {
    150                 onChange();
    151             }
    152         }
    153 
    154         public void onChange() {
    155             // Do not overwrite the default set manually.
    156             if (!SystemProperties.get(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING).isEmpty()) {
    157               return;
    158             }
    159 
    160             try {
    161                     mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
    162                             Settings.Global.BINDER_CALLS_STATS));
    163             } catch (IllegalArgumentException e) {
    164                     Slog.e(TAG, "Bad binder call stats settings", e);
    165             }
    166             mBinderCallsStats.setDetailedTracking(mParser.getBoolean(
    167                     SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
    168             mBinderCallsStats.setSamplingInterval(mParser.getInt(
    169                     SETTINGS_SAMPLING_INTERVAL_KEY,
    170                     BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
    171             mBinderCallsStats.setMaxBinderCallStats(mParser.getInt(
    172                     SETTINGS_MAX_CALL_STATS_KEY,
    173                     BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
    174             mBinderCallsStats.setTrackScreenInteractive(
    175                     mParser.getBoolean(SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY,
    176                     BinderCallsStats.DEFAULT_TRACK_SCREEN_INTERACTIVE));
    177             mBinderCallsStats.setTrackDirectCallerUid(
    178                     mParser.getBoolean(SETTINGS_TRACK_DIRECT_CALLING_UID_KEY,
    179                     BinderCallsStats.DEFAULT_TRACK_DIRECT_CALLING_UID));
    180 
    181 
    182             final boolean enabled =
    183                     mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
    184             if (mEnabled != enabled) {
    185                 if (enabled) {
    186                     Binder.setObserver(mBinderCallsStats);
    187                     Binder.setProxyTransactListener(
    188                             new Binder.PropagateWorkSourceTransactListener());
    189                     Binder.setWorkSourceProvider(mWorkSourceProvider);
    190                 } else {
    191                     Binder.setObserver(null);
    192                     Binder.setProxyTransactListener(null);
    193                     Binder.setWorkSourceProvider((x) -> Binder.getCallingUid());
    194                 }
    195                 mEnabled = enabled;
    196                 mBinderCallsStats.reset();
    197                 mBinderCallsStats.setAddDebugEntries(enabled);
    198             }
    199         }
    200     }
    201 
    202     /**
    203      * @hide Only for use within the system server.
    204      */
    205     public static class Internal {
    206         private final BinderCallsStats mBinderCallsStats;
    207 
    208         Internal(BinderCallsStats binderCallsStats) {
    209             this.mBinderCallsStats = binderCallsStats;
    210         }
    211 
    212         /** @see BinderCallsStats#reset */
    213         public void reset() {
    214             mBinderCallsStats.reset();
    215         }
    216 
    217         /**
    218          * @see BinderCallsStats#getExportedCallStats.
    219          *
    220          * Note that binder calls stats will be reset by statsd every time
    221          * the data is exported.
    222          */
    223         public ArrayList<BinderCallsStats.ExportedCallStat> getExportedCallStats() {
    224             return mBinderCallsStats.getExportedCallStats();
    225         }
    226 
    227         /** @see BinderCallsStats#getExportedExceptionStats */
    228         public ArrayMap<String, Integer> getExportedExceptionStats() {
    229             return mBinderCallsStats.getExportedExceptionStats();
    230         }
    231     }
    232 
    233     public static class LifeCycle extends SystemService {
    234         private BinderCallsStatsService mService;
    235         private BinderCallsStats mBinderCallsStats;
    236         private AuthorizedWorkSourceProvider mWorkSourceProvider;
    237 
    238         public LifeCycle(Context context) {
    239             super(context);
    240         }
    241 
    242         @Override
    243         public void onStart() {
    244             mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
    245             mWorkSourceProvider = new AuthorizedWorkSourceProvider();
    246             mService = new BinderCallsStatsService(
    247                     mBinderCallsStats, mWorkSourceProvider);
    248             publishLocalService(Internal.class, new Internal(mBinderCallsStats));
    249             publishBinderService("binder_calls_stats", mService);
    250             boolean detailedTrackingEnabled = SystemProperties.getBoolean(
    251                     PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
    252 
    253             if (detailedTrackingEnabled) {
    254                 Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
    255                         + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
    256                         + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
    257                 mBinderCallsStats.setDetailedTracking(true);
    258             }
    259         }
    260 
    261         @Override
    262         public void onBootPhase(int phase) {
    263             if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
    264                 CachedDeviceState.Readonly deviceState = getLocalService(
    265                         CachedDeviceState.Readonly.class);
    266                 mBinderCallsStats.setDeviceState(deviceState);
    267                 // It needs to be called before mService.systemReady to make sure the observer is
    268                 // initialized before installing it.
    269                 mWorkSourceProvider.systemReady(getContext());
    270                 mService.systemReady(getContext());
    271             }
    272         }
    273     }
    274 
    275     private SettingsObserver mSettingsObserver;
    276     private final BinderCallsStats mBinderCallsStats;
    277     private final AuthorizedWorkSourceProvider mWorkSourceProvider;
    278 
    279     BinderCallsStatsService(BinderCallsStats binderCallsStats,
    280             AuthorizedWorkSourceProvider workSourceProvider) {
    281         mBinderCallsStats = binderCallsStats;
    282         mWorkSourceProvider = workSourceProvider;
    283     }
    284 
    285     public void systemReady(Context context) {
    286         mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mWorkSourceProvider);
    287     }
    288 
    289     public void reset() {
    290         Slog.i(TAG, "Resetting stats");
    291         mBinderCallsStats.reset();
    292     }
    293 
    294     @Override
    295     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    296         boolean verbose = false;
    297         if (args != null) {
    298             for (final String arg : args) {
    299                 if ("-a".equals(arg)) {
    300                     verbose = true;
    301                 } else if ("--reset".equals(arg)) {
    302                     reset();
    303                     pw.println("binder_calls_stats reset.");
    304                     return;
    305                 } else if ("--enable".equals(arg)) {
    306                     Binder.setObserver(mBinderCallsStats);
    307                     return;
    308                 } else if ("--disable".equals(arg)) {
    309                     Binder.setObserver(null);
    310                     return;
    311                 } else if ("--no-sampling".equals(arg)) {
    312                     mBinderCallsStats.setSamplingInterval(1);
    313                     return;
    314                 } else if ("--enable-detailed-tracking".equals(arg)) {
    315                     SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
    316                     mBinderCallsStats.setDetailedTracking(true);
    317                     pw.println("Detailed tracking enabled");
    318                     return;
    319                 } else if ("--disable-detailed-tracking".equals(arg)) {
    320                     SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "");
    321                     mBinderCallsStats.setDetailedTracking(false);
    322                     pw.println("Detailed tracking disabled");
    323                     return;
    324                 } else if ("--dump-worksource-provider".equals(arg)) {
    325                     mWorkSourceProvider.dump(pw, AppIdToPackageMap.getSnapshot());
    326                     return;
    327                 } else if ("-h".equals(arg)) {
    328                     pw.println("binder_calls_stats commands:");
    329                     pw.println("  --reset: Reset stats");
    330                     pw.println("  --enable: Enable tracking binder calls");
    331                     pw.println("  --disable: Disables tracking binder calls");
    332                     pw.println("  --no-sampling: Tracks all calls");
    333                     pw.println("  --enable-detailed-tracking: Enables detailed tracking");
    334                     pw.println("  --disable-detailed-tracking: Disables detailed tracking");
    335                     return;
    336                 } else {
    337                     pw.println("Unknown option: " + arg);
    338                 }
    339             }
    340         }
    341         mBinderCallsStats.dump(pw, AppIdToPackageMap.getSnapshot(), verbose);
    342     }
    343 }
    344