1 /*
2 * Copyright (C) 2015 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.internal.telephony;
18 
19 import android.annotation.NonNull;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.ServiceConnection;
27 import android.content.pm.ComponentInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.HandlerExecutor;
34 import android.os.IBinder;
35 import android.os.Message;
36 import android.os.Process;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.service.carrier.CarrierService;
40 import android.telephony.SubscriptionManager;
41 import android.telephony.TelephonyManager;
42 import android.text.TextUtils;
43 import android.util.LocalLog;
44 import android.util.Log;
45 import android.util.SparseArray;
46 
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.telephony.util.TelephonyUtils;
49 
50 import java.io.FileDescriptor;
51 import java.io.PrintWriter;
52 import java.util.Arrays;
53 import java.util.Set;
54 
55 /**
56  * Manages long-lived bindings to carrier services
57  * @hide
58  */
59 public class CarrierServiceBindHelper {
60     private static final String LOG_TAG = "CarrierSvcBindHelper";
61 
62     // TODO(b/201423849): Remove the UNBIND_DELAY_MILLIS and switch to CarrierPrivilegesCallback
63     // The grace period has been replaced by CarrierPrivilegesTracker. CarrierPrivilegesCallback has
64     // provided the callback for both carrier privileges change and carrier service change (with
65     // awareness of the grace period), the delay based logic here should be cleaned up.
66     /**
67      * How long to linger a binding after an app loses carrier privileges, as long as no new
68      * binding comes in to take its place.
69      */
70     private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds
71 
72     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
73     private Context mContext;
74     @VisibleForTesting
75     public SparseArray<AppBinding> mBindings = new SparseArray();
76     @VisibleForTesting
77     public SparseArray<String> mLastSimState = new SparseArray<>();
78     // TODO(b/201423849): Clean up PackageChangeReceiver/UserUnlockedReceiver/SIM State change if
79     // CarrierServiceChangeCallback can cover the cases
80     private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor();
81     private final LocalLog mLocalLog = new LocalLog(100);
82 
83     private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
84         @Override
85         public void onReceive(Context context, Intent intent) {
86             final String action = intent.getAction();
87             logdWithLocalLog("Received " + action);
88 
89             if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
90                 // On user unlock, new components might become available, so reevaluate all
91                 // bindings.
92                 for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) {
93                     mBindings.get(phoneId).rebind();
94                 }
95             }
96         }
97     };
98 
99     private class CarrierServiceChangeCallback implements
100             TelephonyManager.CarrierPrivilegesCallback {
101         final int mPhoneId;
102 
CarrierServiceChangeCallback(int phoneId)103         CarrierServiceChangeCallback(int phoneId) {
104             this.mPhoneId = phoneId;
105         }
106 
107         @Override
onCarrierPrivilegesChanged(Set<String> privilegedPackageNames, Set<Integer> privilegedUids)108         public void onCarrierPrivilegesChanged(Set<String> privilegedPackageNames,
109                 Set<Integer> privilegedUids) {
110             // Ignored, not interested here
111         }
112 
113         @Override
onCarrierServiceChanged(String carrierServicePackageName, int carrierServiceUid)114         public void onCarrierServiceChanged(String carrierServicePackageName,
115                 int carrierServiceUid) {
116             logdWithLocalLog("onCarrierServiceChanged, carrierServicePackageName="
117                     + carrierServicePackageName + ", carrierServiceUid=" + carrierServiceUid
118                     + ", mPhoneId=" + mPhoneId);
119             mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mPhoneId));
120         }
121     }
122 
123     private static final int EVENT_REBIND = 0;
124     @VisibleForTesting
125     public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
126     @VisibleForTesting
127     public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2;
128 
129     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
130     @VisibleForTesting
131     public Handler mHandler = new Handler() {
132         @Override
133         public void handleMessage(Message msg) {
134             int phoneId;
135             AppBinding binding;
136             logdWithLocalLog("mHandler: " + msg.what);
137 
138             switch (msg.what) {
139                 case EVENT_REBIND:
140                     phoneId = (int) msg.obj;
141                     binding = mBindings.get(phoneId);
142                     if (binding == null) return;
143                     logdWithLocalLog("Rebinding if necessary for phoneId: " + binding.getPhoneId());
144                     binding.rebind();
145                     break;
146                 case EVENT_PERFORM_IMMEDIATE_UNBIND:
147                     phoneId = (int) msg.obj;
148                     binding = mBindings.get(phoneId);
149                     if (binding == null) return;
150                     logdWithLocalLog("Unbind immediate with phoneId: " + binding.getPhoneId());
151                     binding.performImmediateUnbind();
152                     break;
153                 case EVENT_MULTI_SIM_CONFIG_CHANGED:
154                     updateBindingsAndSimStates();
155                     break;
156                 default:
157                     Log.e(LOG_TAG, "Unsupported event received: " + msg.what);
158             }
159         }
160     };
161 
CarrierServiceBindHelper(Context context)162     public CarrierServiceBindHelper(Context context) {
163         mContext = context.createContextAsUser(Process.myUserHandle(), 0);
164 
165         updateBindingsAndSimStates();
166 
167         PhoneConfigurationManager.registerForMultiSimConfigChange(
168                 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
169 
170         mPackageMonitor.register(
171                 context, mHandler.getLooper(), UserHandle.ALL);
172         try {
173             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
174                 0, UserHandle.SYSTEM);
175             contextAsUser.registerReceiver(mUserUnlockedReceiver,
176                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
177                 mHandler);
178         } catch (PackageManager.NameNotFoundException e) {
179             logeWithLocalLog("Package name not found: " + e.getMessage());
180         }
181     }
182 
183     // Create or dispose mBindings and mLastSimState objects.
updateBindingsAndSimStates()184     private void updateBindingsAndSimStates() {
185         int prevLen = mBindings.size();
186         int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
187                 .getActiveModemCount();
188 
189         // If prevLen < newLen, allocate AppBinding and simState objects.
190         for (int phoneId = prevLen; phoneId < newLen; phoneId++) {
191             mBindings.put(phoneId, new AppBinding(phoneId));
192             mLastSimState.put(phoneId, new String());
193         }
194 
195         // If prevLen > newLen, dispose AppBinding and simState objects.
196         for (int phoneId = newLen; phoneId < prevLen; phoneId++) {
197             mBindings.get(phoneId).tearDown();
198             mBindings.get(phoneId).unbind(true);
199             mBindings.delete(phoneId);
200             mLastSimState.delete(phoneId);
201         }
202     }
203 
204     /**
205      * Update SIM state.
206      *
207      * @param phoneId The phone id.
208      * @param simState The legacy SIM state.
209      */
updateForPhoneId(int phoneId, @NonNull String simState)210     public void updateForPhoneId(int phoneId, @NonNull String simState) {
211         logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState);
212         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
213             return;
214         }
215         if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return;
216         if (simState.equals(mLastSimState.get(phoneId))) {
217             // ignore consecutive duplicated events
218             return;
219         } else {
220             mLastSimState.put(phoneId, simState);
221         }
222         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId));
223     }
224 
225     private class AppBinding {
226         private int phoneId;
227         private CarrierServiceConnection connection;
228         private int bindCount;
229         private long lastBindStartMillis;
230         private int unbindCount;
231         private long lastUnbindMillis;
232         private String carrierPackage;
233         private String carrierServiceClass;
234         private long mUnbindScheduledUptimeMillis = -1;
235         private final CarrierServiceChangeCallback mCarrierServiceChangeCallback;
236 
AppBinding(int phoneId)237         public AppBinding(int phoneId) {
238             this.phoneId = phoneId;
239             this.mCarrierServiceChangeCallback = new CarrierServiceChangeCallback(phoneId);
240             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
241             if (tm != null) {
242                 tm.registerCarrierPrivilegesCallback(phoneId, new HandlerExecutor(mHandler),
243                         mCarrierServiceChangeCallback);
244             }
245         }
246 
tearDown()247         public void tearDown() {
248             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
249             if (tm != null && mCarrierServiceChangeCallback != null) {
250                 tm.unregisterCarrierPrivilegesCallback(mCarrierServiceChangeCallback);
251             }
252         }
253 
getPhoneId()254         public int getPhoneId() {
255             return phoneId;
256         }
257 
258         /** Return the package that is currently being bound to, or null if there is no binding. */
getPackage()259         public String getPackage() {
260             return carrierPackage;
261         }
262 
263         /**
264          * Update the bindings for the current carrier app for this phone.
265          *
266          * <p>Safe to call even if a binding already exists. If the current binding is invalid, it
267          * will be dropped. If it is valid, it will be left untouched.
268          */
rebind()269         void rebind() {
270             // Get the package name for the carrier app
271             String carrierPackageName = TelephonyManager.from(
272                     mContext).getCarrierServicePackageNameForLogicalSlot(phoneId);
273 
274             if (carrierPackageName == null) {
275                 logdWithLocalLog("No carrier app for: " + phoneId);
276                 // Unbind after a delay in case this is a temporary blip in carrier privileges.
277                 unbind(false /* immediate */);
278                 return;
279             }
280 
281             logdWithLocalLog("Found carrier app: " + carrierPackageName);
282             // If we are binding to a different package, unbind immediately from the current one.
283             if (!TextUtils.equals(carrierPackage, carrierPackageName)) {
284                 unbind(true /* immediate */);
285             }
286 
287             // Look up the carrier service
288             Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
289             carrierService.setPackage(carrierPackageName);
290 
291             ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
292                 carrierService, PackageManager.GET_META_DATA);
293             Bundle metadata = null;
294             String candidateServiceClass = null;
295             if (carrierResolveInfo != null) {
296                 metadata = carrierResolveInfo.serviceInfo.metaData;
297                 ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo);
298                 candidateServiceClass = new ComponentName(componentInfo.packageName,
299                     componentInfo.name).getClassName();
300             }
301 
302             // Only bind if the service wants it
303             if (metadata == null ||
304                 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
305                 logdWithLocalLog("Carrier app does not want a long lived binding");
306                 unbind(true /* immediate */);
307                 return;
308             }
309 
310             if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) {
311                 logdWithLocalLog("CarrierService class changed, unbind immediately.");
312                 // Unbind immediately if the carrier service component has changed.
313                 unbind(true /* immediate */);
314             } else if (connection != null) {
315                 logdWithLocalLog(
316                         "CarrierService class unchanged with connection up, cancelScheduledUnbind");
317                 // Component is unchanged and connection is up - do nothing, but cancel any
318                 // scheduled unbinds.
319                 cancelScheduledUnbind();
320                 return;
321             }
322 
323             carrierPackage = carrierPackageName;
324             carrierServiceClass = candidateServiceClass;
325 
326             logdWithLocalLog("Binding to " + carrierPackage + " for phone " + phoneId);
327 
328             // Log debug information
329             bindCount++;
330             lastBindStartMillis = System.currentTimeMillis();
331 
332             connection = new CarrierServiceConnection();
333 
334             String error;
335             try {
336                 if (mContext.bindService(
337                         carrierService,
338                         Context.BIND_AUTO_CREATE
339                                 | Context.BIND_FOREGROUND_SERVICE
340                                 | Context.BIND_INCLUDE_CAPABILITIES,
341                         (r) -> mHandler.post(r),
342                         connection)) {
343                     logdWithLocalLog("service bound");
344                     return;
345                 }
346 
347                 error = "bindService returned false";
348             } catch (SecurityException ex) {
349                 error = ex.getMessage();
350             }
351 
352             logdWithLocalLog("Unable to bind to " + carrierPackage + " for phone " + phoneId
353                     + ". Error: " + error);
354             unbind(true /* immediate */);
355         }
356 
357         /**
358          * Release the binding.
359          *
360          * @param immediate whether the binding should be released immediately or after a short
361          *                  delay. This should be true unless the reason for the unbind is that no
362          *                  app has carrier privileges, in which case it is useful to delay
363          *                  unbinding in case this is a temporary SIM blip.
364          */
unbind(boolean immediate)365         void unbind(boolean immediate) {
366             if (connection == null) {
367                 // Already fully unbound.
368                 return;
369             }
370 
371             // Only let the binding linger if a delayed unbind is requested *and* the connection is
372             // currently active. If the connection is down, unbind immediately as the app is likely
373             // not running anyway and it may be a permanent disconnection (e.g. the app was
374             // disabled).
375             if (immediate || !connection.connected) {
376                 logdWithLocalLog("unbind immediately or with disconnected connection");
377                 cancelScheduledUnbind();
378                 performImmediateUnbind();
379             } else if (mUnbindScheduledUptimeMillis == -1) {
380                 long currentUptimeMillis = SystemClock.uptimeMillis();
381                 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
382                 logdWithLocalLog("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
383                 mHandler.sendMessageAtTime(
384                         mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId),
385                         mUnbindScheduledUptimeMillis);
386             }
387         }
388 
performImmediateUnbind()389         private void performImmediateUnbind() {
390             // Log debug information
391             unbindCount++;
392             lastUnbindMillis = System.currentTimeMillis();
393 
394             // Clear package state now that no binding is desired.
395             carrierPackage = null;
396             carrierServiceClass = null;
397 
398             // Always call unbindService, no matter if bindService succeed.
399             if (connection != null) {
400                 mContext.unbindService(connection);
401                 logdWithLocalLog("Unbinding from carrier app");
402                 connection = null;
403                 mUnbindScheduledUptimeMillis = -1;
404             }
405         }
406 
cancelScheduledUnbind()407         private void cancelScheduledUnbind() {
408             logdWithLocalLog("cancelScheduledUnbind");
409             mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND);
410             mUnbindScheduledUptimeMillis = -1;
411         }
412 
dump(FileDescriptor fd, PrintWriter pw, String[] args)413         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
414             pw.println("Carrier app binding for phone " + phoneId);
415             pw.println("  connection: " + connection);
416             pw.println("  bindCount: " + bindCount);
417             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
418             pw.println("  unbindCount: " + unbindCount);
419             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
420             pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
421             pw.println("  mCarrierServiceChangeCallback: " + mCarrierServiceChangeCallback);
422             pw.println();
423         }
424     }
425 
426     private class CarrierServiceConnection implements ServiceConnection {
427         private boolean connected;
428 
429         @Override
onServiceConnected(ComponentName name, IBinder service)430         public void onServiceConnected(ComponentName name, IBinder service) {
431             logdWithLocalLog("Connected to carrier app: " + name.flattenToString());
432             connected = true;
433         }
434 
435         @Override
onServiceDisconnected(ComponentName name)436         public void onServiceDisconnected(ComponentName name) {
437             logdWithLocalLog("Disconnected from carrier app: " + name.flattenToString());
438             connected = false;
439         }
440 
441         @Override
onBindingDied(ComponentName name)442         public void onBindingDied(ComponentName name) {
443             logdWithLocalLog("Binding from carrier app died: " + name.flattenToString());
444             connected = false;
445         }
446 
447         @Override
onNullBinding(ComponentName name)448         public void onNullBinding(ComponentName name) {
449             logdWithLocalLog("Null binding from carrier app: " + name.flattenToString());
450             connected = false;
451         }
452 
453         @Override
toString()454         public String toString() {
455             return "CarrierServiceConnection[connected=" + connected + "]";
456         }
457     }
458 
459     private class CarrierServicePackageMonitor extends PackageChangeReceiver {
460         @Override
onPackageAdded(String packageName)461         public void onPackageAdded(String packageName) {
462             logdWithLocalLog("onPackageAdded: " + packageName);
463             evaluateBinding(packageName, true /* forceUnbind */);
464         }
465 
466         @Override
onPackageRemoved(String packageName)467         public void onPackageRemoved(String packageName) {
468             logdWithLocalLog("onPackageRemoved: " + packageName);
469             evaluateBinding(packageName, true /* forceUnbind */);
470         }
471 
472         @Override
onPackageUpdateFinished(String packageName)473         public void onPackageUpdateFinished(String packageName) {
474             logdWithLocalLog("onPackageUpdateFinished: " + packageName);
475             evaluateBinding(packageName, true /* forceUnbind */);
476         }
477 
478         @Override
onPackageModified(String packageName)479         public void onPackageModified(String packageName) {
480             logdWithLocalLog("onPackageModified: " + packageName);
481             evaluateBinding(packageName, false /* forceUnbind */);
482         }
483 
484         @Override
onHandleForceStop(String[] packages, boolean doit)485         public void onHandleForceStop(String[] packages, boolean doit) {
486             if (doit) {
487                 logdWithLocalLog("onHandleForceStop: " + Arrays.toString(packages));
488                 for (String packageName : packages) {
489                     evaluateBinding(packageName, true /* forceUnbind */);
490                 }
491             }
492         }
493 
evaluateBinding(String carrierPackageName, boolean forceUnbind)494         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
495             for (int i = 0; i < mBindings.size(); i++) {
496                 AppBinding appBinding = mBindings.get(i);
497                 String appBindingPackage = appBinding.getPackage();
498                 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
499                 // Only log if this package was a carrier package to avoid log spam in the common
500                 // case that there are no carrier packages, but evaluate the binding if the package
501                 // is unset, in case this package change resulted in a new carrier package becoming
502                 // available for binding.
503                 if (isBindingForPackage) {
504                     logdWithLocalLog(
505                             carrierPackageName + " changed and corresponds to a phone. Rebinding.");
506                 }
507                 if (appBindingPackage == null || isBindingForPackage) {
508                     if (forceUnbind) {
509                         appBinding.unbind(true /* immediate */);
510                     }
511                     appBinding.rebind();
512                 }
513             }
514         }
515     }
516 
logdWithLocalLog(String msg)517     private void logdWithLocalLog(String msg) {
518         Log.d(LOG_TAG, msg);
519         mLocalLog.log(msg);
520     }
521 
logeWithLocalLog(String msg)522     private void logeWithLocalLog(String msg) {
523         Log.e(LOG_TAG, msg);
524         mLocalLog.log(msg);
525     }
526 
dump(FileDescriptor fd, PrintWriter pw, String[] args)527     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
528         pw.println("CarrierServiceBindHelper:");
529         for (int i = 0; i < mBindings.size(); i++) {
530             mBindings.get(i).dump(fd, pw, args);
531         }
532         pw.println("CarrierServiceBindHelperLogs=");
533         mLocalLog.dump(fd, pw, args);
534     }
535 }
536