Home | History | Annotate | Download | only in appbinding
      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.appbinding;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.app.AppGlobals;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.IPackageManager;
     29 import android.content.pm.ServiceInfo;
     30 import android.database.ContentObserver;
     31 import android.net.Uri;
     32 import android.os.Binder;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.IInterface;
     36 import android.os.UserHandle;
     37 import android.provider.Settings;
     38 import android.provider.Settings.Global;
     39 import android.text.TextUtils;
     40 import android.util.Slog;
     41 import android.util.SparseBooleanArray;
     42 
     43 import com.android.internal.annotations.GuardedBy;
     44 import com.android.internal.annotations.VisibleForTesting;
     45 import com.android.internal.os.BackgroundThread;
     46 import com.android.internal.util.DumpUtils;
     47 import com.android.server.SystemService;
     48 import com.android.server.am.PersistentConnection;
     49 import com.android.server.appbinding.finders.AppServiceFinder;
     50 import com.android.server.appbinding.finders.CarrierMessagingClientServiceFinder;
     51 
     52 import java.io.FileDescriptor;
     53 import java.io.PrintWriter;
     54 import java.util.ArrayList;
     55 import java.util.function.Consumer;
     56 
     57 /**
     58  * System server that keeps a binding to an app to keep it always running.
     59  *
     60  * <p>As of android Q, we only use it for the default SMS app.
     61  *
     62  * Relevant tests:
     63  * atest CtsAppBindingHostTestCases
     64  *
     65  * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind
     66  * after a timeout. b/116813347
     67  */
     68 public class AppBindingService extends Binder {
     69     public static final String TAG = "AppBindingService";
     70 
     71     public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
     72 
     73     private final Object mLock = new Object();
     74 
     75     private final Injector mInjector;
     76     private final Context mContext;
     77     private final Handler mHandler;
     78     private final IPackageManager mIPackageManager;
     79 
     80     @GuardedBy("mLock")
     81     private AppBindingConstants mConstants;
     82 
     83     @GuardedBy("mLock")
     84     private final SparseBooleanArray mRunningUsers = new SparseBooleanArray(2);
     85 
     86     @GuardedBy("mLock")
     87     private final ArrayList<AppServiceFinder> mApps = new ArrayList<>();
     88 
     89     @GuardedBy("mLock")
     90     private final ArrayList<AppServiceConnection> mConnections = new ArrayList<>();
     91 
     92     static class Injector {
     93         public IPackageManager getIPackageManager() {
     94             return AppGlobals.getPackageManager();
     95         }
     96 
     97         public String getGlobalSettingString(ContentResolver resolver, String key) {
     98             return Settings.Global.getString(resolver, key);
     99         }
    100     }
    101 
    102     /**
    103      * {@link SystemService} for this service.
    104      */
    105     public static class Lifecycle extends SystemService {
    106         final AppBindingService mService;
    107 
    108         public Lifecycle(Context context) {
    109             this(context, new Injector());
    110         }
    111 
    112         Lifecycle(Context context, Injector injector) {
    113             super(context);
    114             mService = new AppBindingService(injector, context);
    115         }
    116 
    117         @Override
    118         public void onStart() {
    119             publishBinderService(Context.APP_BINDING_SERVICE, mService);
    120         }
    121 
    122         @Override
    123         public void onBootPhase(int phase) {
    124             mService.onBootPhase(phase);
    125         }
    126 
    127         @Override
    128         public void onStartUser(int userHandle) {
    129             mService.onStartUser(userHandle);
    130         }
    131 
    132         @Override
    133         public void onUnlockUser(int userId) {
    134             mService.onUnlockUser(userId);
    135         }
    136 
    137         @Override
    138         public void onStopUser(int userHandle) {
    139             mService.onStopUser(userHandle);
    140         }
    141     }
    142 
    143     private AppBindingService(Injector injector, Context context) {
    144         mInjector = injector;
    145         mContext = context;
    146 
    147         mIPackageManager = injector.getIPackageManager();
    148 
    149         mHandler = BackgroundThread.getHandler();
    150         mApps.add(new CarrierMessagingClientServiceFinder(context, this::onAppChanged, mHandler));
    151 
    152         // Initialize with the default value to make it non-null.
    153         mConstants = AppBindingConstants.initializeFromString("");
    154     }
    155 
    156     private void forAllAppsLocked(Consumer<AppServiceFinder> consumer) {
    157         for (int i = 0; i < mApps.size(); i++) {
    158             consumer.accept(mApps.get(i));
    159         }
    160     }
    161 
    162     private void onBootPhase(int phase) {
    163         if (DEBUG) {
    164             Slog.d(TAG, "onBootPhase: " + phase);
    165         }
    166         switch (phase) {
    167             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
    168                 onPhaseActivityManagerReady();
    169                 break;
    170             case SystemService.PHASE_THIRD_PARTY_APPS_CAN_START:
    171                 onPhaseThirdPartyAppsCanStart();
    172                 break;
    173         }
    174     }
    175 
    176     /**
    177      * Handle boot phase PHASE_ACTIVITY_MANAGER_READY.
    178      */
    179     private void onPhaseActivityManagerReady() {
    180         // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades.
    181         // (app uninstall/disable will be notified by RoleManager.)
    182         final IntentFilter packageFilter = new IntentFilter();
    183         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    184         packageFilter.addDataScheme("package");
    185 
    186         mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
    187                 packageFilter, null, mHandler);
    188 
    189         final IntentFilter userFilter = new IntentFilter();
    190         userFilter.addAction(Intent.ACTION_USER_REMOVED);
    191         mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
    192                 userFilter, null, mHandler);
    193 
    194         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    195                 Settings.Global.APP_BINDING_CONSTANTS), false, mSettingsObserver);
    196 
    197         refreshConstants();
    198     }
    199 
    200     private final ContentObserver mSettingsObserver = new ContentObserver(null) {
    201         @Override
    202         public void onChange(boolean selfChange) {
    203             refreshConstants();
    204         }
    205     };
    206 
    207     private void refreshConstants() {
    208         final String newSetting = mInjector.getGlobalSettingString(
    209                 mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);
    210 
    211         synchronized (mLock) {
    212             if (TextUtils.equals(mConstants.sourceSettings, newSetting)) {
    213                 return;
    214             }
    215             Slog.i(TAG, "Updating constants with: " + newSetting);
    216             mConstants = AppBindingConstants.initializeFromString(newSetting);
    217 
    218             rebindAllLocked("settings update");
    219         }
    220     }
    221 
    222     @VisibleForTesting
    223     final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
    224         @Override
    225         public void onReceive(Context context, Intent intent) {
    226             if (DEBUG) {
    227                 Slog.d(TAG, "Broadcast received: " + intent);
    228             }
    229             final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
    230             if (userId == UserHandle.USER_NULL) {
    231                 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
    232                 return;
    233             }
    234 
    235             final String action = intent.getAction();
    236 
    237             if (Intent.ACTION_USER_REMOVED.equals(action)) {
    238                 onUserRemoved(userId);
    239                 return;
    240             }
    241 
    242             final Uri intentUri = intent.getData();
    243             final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
    244                     : null;
    245             if (packageName == null) {
    246                 Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
    247                 return;
    248             }
    249 
    250             final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
    251 
    252             switch (action) {
    253                 case Intent.ACTION_PACKAGE_ADDED:
    254                     if (replacing) {
    255                         handlePackageAddedReplacing(packageName, userId);
    256                     }
    257                     break;
    258             }
    259         }
    260     };
    261 
    262     /**
    263      * Handle boot phase PHASE_THIRD_PARTY_APPS_CAN_START.
    264      */
    265     private void onPhaseThirdPartyAppsCanStart() {
    266         synchronized (mLock) {
    267             forAllAppsLocked(AppServiceFinder::startMonitoring);
    268         }
    269     }
    270 
    271     /** User lifecycle callback. */
    272     private void onStartUser(int userId) {
    273         if (DEBUG) {
    274             Slog.d(TAG, "onStartUser: u" + userId);
    275         }
    276         synchronized (mLock) {
    277             mRunningUsers.append(userId, true);
    278             bindServicesLocked(userId, null, "user start");
    279         }
    280     }
    281 
    282     /** User lifecycle callback. */
    283     private void onUnlockUser(int userId) {
    284         if (DEBUG) {
    285             Slog.d(TAG, "onUnlockUser: u" + userId);
    286         }
    287         synchronized (mLock) {
    288             bindServicesLocked(userId, null, "user unlock");
    289         }
    290     }
    291 
    292     /** User lifecycle callback. */
    293     private void onStopUser(int userId) {
    294         if (DEBUG) {
    295             Slog.d(TAG, "onStopUser: u" + userId);
    296         }
    297         synchronized (mLock) {
    298             unbindServicesLocked(userId, null, "user stop");
    299 
    300             mRunningUsers.delete(userId);
    301         }
    302     }
    303 
    304     private void onUserRemoved(int userId) {
    305         if (DEBUG) {
    306             Slog.d(TAG, "onUserRemoved: u" + userId);
    307         }
    308         synchronized (mLock) {
    309             forAllAppsLocked((app) -> app.onUserRemoved(userId));
    310 
    311             mRunningUsers.delete(userId);
    312         }
    313     }
    314 
    315     /**
    316      * Called when a target package changes; e.g. when the user changes the default SMS app.
    317      */
    318     private void onAppChanged(AppServiceFinder finder, int userId) {
    319         if (DEBUG) {
    320             Slog.d(TAG, "onAppChanged: u" + userId + " " + finder.getAppDescription());
    321         }
    322         synchronized (mLock) {
    323             final String reason = finder.getAppDescription() + " changed";
    324             unbindServicesLocked(userId, finder, reason);
    325             bindServicesLocked(userId, finder, reason);
    326         }
    327     }
    328 
    329     @Nullable
    330     private AppServiceFinder findFinderLocked(int userId, @NonNull String packageName) {
    331         for (int i = 0; i < mApps.size(); i++) {
    332             final AppServiceFinder app = mApps.get(i);
    333             if (packageName.equals(app.getTargetPackage(userId))) {
    334                 return app;
    335             }
    336         }
    337         return null;
    338     }
    339 
    340     @Nullable
    341     private AppServiceConnection findConnectionLock(
    342             int userId, @NonNull AppServiceFinder target) {
    343         for (int i = 0; i < mConnections.size(); i++) {
    344             final AppServiceConnection conn = mConnections.get(i);
    345             if ((conn.getUserId() == userId) && (conn.getFinder() == target)) {
    346                 return conn;
    347             }
    348         }
    349         return null;
    350     }
    351 
    352     private void handlePackageAddedReplacing(String packageName, int userId) {
    353         if (DEBUG) {
    354             Slog.d(TAG, "handlePackageAddedReplacing: u" + userId + " " + packageName);
    355         }
    356         synchronized (mLock) {
    357             final AppServiceFinder finder = findFinderLocked(userId, packageName);
    358             if (finder != null) {
    359                 unbindServicesLocked(userId, finder, "package update");
    360                 bindServicesLocked(userId, finder, "package update");
    361             }
    362         }
    363     }
    364 
    365     private void rebindAllLocked(String reason) {
    366         for (int i = 0; i < mRunningUsers.size(); i++) {
    367             if (!mRunningUsers.valueAt(i)) {
    368                 continue;
    369             }
    370             final int userId = mRunningUsers.keyAt(i);
    371 
    372             unbindServicesLocked(userId, null, reason);
    373             bindServicesLocked(userId, null, reason);
    374         }
    375     }
    376 
    377     private void bindServicesLocked(int userId, @Nullable AppServiceFinder target,
    378             @NonNull String reasonForLog) {
    379         for (int i = 0; i < mApps.size(); i++) {
    380             final AppServiceFinder app = mApps.get(i);
    381             if (target != null && target != app) {
    382                 continue;
    383             }
    384 
    385             // Disconnect from existing binding.
    386             final AppServiceConnection existingConn = findConnectionLock(userId, app);
    387             if (existingConn != null) {
    388                 unbindServicesLocked(userId, target, reasonForLog);
    389             }
    390 
    391             final ServiceInfo service = app.findService(userId, mIPackageManager, mConstants);
    392             if (service == null) {
    393                 continue;
    394             }
    395             if (DEBUG) {
    396                 Slog.d(TAG, "bindServicesLocked: u" + userId + " " + app.getAppDescription()
    397                         + " binding " + service.getComponentName() + " for " + reasonForLog);
    398             }
    399             final AppServiceConnection conn =
    400                     new AppServiceConnection(mContext, userId, mConstants, mHandler,
    401                             app, service.getComponentName());
    402             mConnections.add(conn);
    403             conn.bind();
    404         }
    405     }
    406 
    407     private void unbindServicesLocked(int userId, @Nullable AppServiceFinder target,
    408             @NonNull String reasonForLog) {
    409         for (int i = mConnections.size() - 1; i >= 0; i--) {
    410             final AppServiceConnection conn = mConnections.get(i);
    411             if ((conn.getUserId() != userId)
    412                     || (target != null && conn.getFinder() != target)) {
    413                 continue;
    414             }
    415             if (DEBUG) {
    416                 Slog.d(TAG, "unbindServicesLocked: u" + userId
    417                         + " " + conn.getFinder().getAppDescription()
    418                         + " unbinding " + conn.getComponentName() + " for " + reasonForLog);
    419             }
    420             mConnections.remove(i);
    421             conn.unbind();
    422         }
    423     }
    424 
    425     private static class AppServiceConnection extends PersistentConnection<IInterface> {
    426         private final AppBindingConstants mConstants;
    427         private final AppServiceFinder mFinder;
    428 
    429         AppServiceConnection(Context context, int userId, AppBindingConstants constants,
    430                 Handler handler, AppServiceFinder finder,
    431                 @NonNull ComponentName componentName) {
    432             super(TAG, context, handler, userId, componentName,
    433                     constants.SERVICE_RECONNECT_BACKOFF_SEC,
    434                     constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
    435                     constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC,
    436                     constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
    437             mFinder = finder;
    438             mConstants = constants;
    439         }
    440 
    441         @Override
    442         protected int getBindFlags() {
    443             return mFinder.getBindFlags(mConstants);
    444         }
    445 
    446         @Override
    447         protected IInterface asInterface(IBinder obj) {
    448             return mFinder.asInterface(obj);
    449         }
    450 
    451         public AppServiceFinder getFinder() {
    452             return mFinder;
    453         }
    454     }
    455 
    456     @Override
    457     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    458         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
    459 
    460         if (args.length > 0 && "-s".equals(args[0])) {
    461             dumpSimple(pw);
    462             return;
    463         }
    464 
    465         synchronized (mLock) {
    466             mConstants.dump("  ", pw);
    467 
    468             pw.println();
    469             pw.print("  Running users:");
    470             for (int i = 0; i < mRunningUsers.size(); i++) {
    471                 if (mRunningUsers.valueAt(i)) {
    472                     pw.print(" ");
    473                     pw.print(mRunningUsers.keyAt(i));
    474                 }
    475             }
    476 
    477             pw.println();
    478             pw.println("  Connections:");
    479             for (int i = 0; i < mConnections.size(); i++) {
    480                 final AppServiceConnection conn = mConnections.get(i);
    481                 pw.print("    App type: ");
    482                 pw.print(conn.getFinder().getAppDescription());
    483                 pw.println();
    484 
    485                 conn.dump("      ", pw);
    486             }
    487             if (mConnections.size() == 0) {
    488                 pw.println("    None:");
    489             }
    490 
    491             pw.println();
    492             pw.println("  Finders:");
    493             forAllAppsLocked((app) -> app.dump("    ", pw));
    494         }
    495     }
    496 
    497     /**
    498      * Print simple output for CTS.
    499      */
    500     private void dumpSimple(PrintWriter pw) {
    501         synchronized (mLock) {
    502             for (int i = 0; i < mConnections.size(); i++) {
    503                 final AppServiceConnection conn = mConnections.get(i);
    504 
    505                 pw.print("conn,");
    506                 pw.print(conn.getFinder().getAppDescription());
    507                 pw.print(",");
    508                 pw.print(conn.getUserId());
    509                 pw.print(",");
    510                 pw.print(conn.getComponentName().getPackageName());
    511                 pw.print(",");
    512                 pw.print(conn.getComponentName().getClassName());
    513                 pw.print(",");
    514                 pw.print(conn.isBound() ? "bound" : "not-bound");
    515                 pw.print(",");
    516                 pw.print(conn.isConnected() ? "connected" : "not-connected");
    517                 pw.print(",#con=");
    518                 pw.print(conn.getNumConnected());
    519                 pw.print(",#dis=");
    520                 pw.print(conn.getNumDisconnected());
    521                 pw.print(",#died=");
    522                 pw.print(conn.getNumBindingDied());
    523                 pw.print(",backoff=");
    524                 pw.print(conn.getNextBackoffMs());
    525                 pw.println();
    526             }
    527             forAllAppsLocked((app) -> app.dumpSimple(pw));
    528         }
    529     }
    530 
    531     AppBindingConstants getConstantsForTest() {
    532         return mConstants;
    533     }
    534 }
    535