• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.ServiceConnection;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Message;
31 import android.os.UserHandle;
32 import android.service.carrier.CarrierService;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.text.TextUtils;
36 import android.util.Log;
37 
38 import com.android.internal.content.PackageMonitor;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.List;
43 
44 /**
45  * Manages long-lived bindings to carrier services
46  * @hide
47  */
48 public class CarrierServiceBindHelper {
49     private static final String LOG_TAG = "CarrierSvcBindHelper";
50 
51     private Context mContext;
52     private AppBinding[] mBindings;
53     private String[] mLastSimState;
54     private final PackageMonitor mPackageMonitor = new CarrierServicePackageMonitor();
55 
56     private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
57         @Override
58         public void onReceive(Context context, Intent intent) {
59             final String action = intent.getAction();
60             log("Received " + action);
61 
62             if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
63                 // On user unlock, new components might become available, so reevaluate all
64                 // bindings.
65                 for (int phoneId = 0; phoneId < mBindings.length; phoneId++) {
66                     mBindings[phoneId].rebind();
67                 }
68             }
69         }
70     };
71 
72     private static final int EVENT_REBIND = 0;
73 
74     private Handler mHandler = new Handler() {
75         @Override
76         public void handleMessage(Message msg) {
77             AppBinding binding;
78             log("mHandler: " + msg.what);
79 
80             switch (msg.what) {
81                 case EVENT_REBIND:
82                     binding = (AppBinding) msg.obj;
83                     log("Rebinding if necessary for phoneId: " + binding.getPhoneId());
84                     binding.rebind();
85                     break;
86             }
87         }
88     };
89 
CarrierServiceBindHelper(Context context)90     public CarrierServiceBindHelper(Context context) {
91         mContext = context;
92 
93         int numPhones = TelephonyManager.from(context).getPhoneCount();
94         mBindings = new AppBinding[numPhones];
95         mLastSimState = new String[numPhones];
96 
97         for (int phoneId = 0; phoneId < numPhones; phoneId++) {
98             mBindings[phoneId] = new AppBinding(phoneId);
99         }
100 
101         mPackageMonitor.register(
102                 context, mHandler.getLooper(), UserHandle.ALL, false /* externalStorage */);
103         mContext.registerReceiverAsUser(mUserUnlockedReceiver, UserHandle.SYSTEM,
104                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
105                 mHandler);
106     }
107 
updateForPhoneId(int phoneId, String simState)108     void updateForPhoneId(int phoneId, String simState) {
109         log("update binding for phoneId: " + phoneId + " simState: " + simState);
110         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
111             return;
112         }
113         if (TextUtils.isEmpty(simState)) return;
114         if (simState.equals(mLastSimState[phoneId])) {
115             // ignore consecutive duplicated events
116             return;
117         } else {
118             mLastSimState[phoneId] = simState;
119         }
120         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mBindings[phoneId]));
121     }
122 
123     private class AppBinding {
124         private int phoneId;
125         private CarrierServiceConnection connection;
126         private int bindCount;
127         private long lastBindStartMillis;
128         private int unbindCount;
129         private long lastUnbindMillis;
130         private String carrierPackage;
131         private String carrierServiceClass;
132 
AppBinding(int phoneId)133         public AppBinding(int phoneId) {
134             this.phoneId = phoneId;
135         }
136 
getPhoneId()137         public int getPhoneId() {
138             return phoneId;
139         }
140 
141         /** Return the package that is currently being bound to, or null if there is no binding. */
getPackage()142         public String getPackage() {
143             return carrierPackage;
144         }
145 
146         /**
147          * Update the bindings for the current carrier app for this phone.
148          *
149          * <p>Safe to call even if a binding already exists. If the current binding is invalid, it
150          * will be dropped. If it is valid, it will be left untouched.
151          */
rebind()152         void rebind() {
153             // Get the package name for the carrier app
154             List<String> carrierPackageNames =
155                 TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone(
156                     new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId
157                 );
158 
159             if (carrierPackageNames == null || carrierPackageNames.size() <= 0) {
160                 log("No carrier app for: " + phoneId);
161                 unbind();
162                 return;
163             }
164 
165             log("Found carrier app: " + carrierPackageNames);
166             String candidateCarrierPackage = carrierPackageNames.get(0);
167             // If we are binding to a different package, unbind from the current one.
168             if (!TextUtils.equals(carrierPackage, candidateCarrierPackage)) {
169                 unbind();
170             }
171 
172             // Look up the carrier service
173             Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
174             carrierService.setPackage(candidateCarrierPackage);
175 
176             ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
177                 carrierService, PackageManager.GET_META_DATA);
178             Bundle metadata = null;
179             String candidateServiceClass = null;
180             if (carrierResolveInfo != null) {
181                 metadata = carrierResolveInfo.serviceInfo.metaData;
182                 candidateServiceClass =
183                         carrierResolveInfo.getComponentInfo().getComponentName().getClassName();
184             }
185 
186             // Only bind if the service wants it
187             if (metadata == null ||
188                 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
189                 log("Carrier app does not want a long lived binding");
190                 unbind();
191                 return;
192             }
193 
194             if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) {
195                 // Unbind if the carrier service component has changed.
196                 unbind();
197             } else if (connection != null) {
198                 // Component is unchanged and connection is up - do nothing.
199                 return;
200             }
201 
202             carrierPackage = candidateCarrierPackage;
203             carrierServiceClass = candidateServiceClass;
204 
205             log("Binding to " + carrierPackage + " for phone " + phoneId);
206 
207             // Log debug information
208             bindCount++;
209             lastBindStartMillis = System.currentTimeMillis();
210 
211             connection = new CarrierServiceConnection();
212 
213             String error;
214             try {
215                 if (mContext.bindService(carrierService, connection, Context.BIND_AUTO_CREATE |
216                             Context.BIND_FOREGROUND_SERVICE)) {
217                     return;
218                 }
219 
220                 error = "bindService returned false";
221             } catch (SecurityException ex) {
222                 error = ex.getMessage();
223             }
224 
225             log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
226                 ". Error: " + error);
227             unbind();
228         }
229 
unbind()230         void unbind() {
231             if (connection == null) {
232                 return;
233             }
234 
235             // Log debug information
236             unbindCount++;
237             lastUnbindMillis = System.currentTimeMillis();
238 
239             // Clear package state now that no binding is present.
240             carrierPackage = null;
241             carrierServiceClass = null;
242 
243             // Actually unbind
244             log("Unbinding from carrier app");
245             mContext.unbindService(connection);
246             connection = null;
247         }
248 
dump(FileDescriptor fd, PrintWriter pw, String[] args)249         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
250             pw.println("Carrier app binding for phone " + phoneId);
251             pw.println("  connection: " + connection);
252             pw.println("  bindCount: " + bindCount);
253             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
254             pw.println("  unbindCount: " + unbindCount);
255             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
256             pw.println();
257         }
258     }
259 
260     private class CarrierServiceConnection implements ServiceConnection {
261         private boolean connected;
262 
263         @Override
onServiceConnected(ComponentName name, IBinder service)264         public void onServiceConnected(ComponentName name, IBinder service) {
265             log("Connected to carrier app: " + name.flattenToString());
266             connected = true;
267         }
268 
269         @Override
onServiceDisconnected(ComponentName name)270         public void onServiceDisconnected(ComponentName name) {
271             log("Disconnected from carrier app: " + name.flattenToString());
272             connected = false;
273         }
274 
275         @Override
toString()276         public String toString() {
277             return "CarrierServiceConnection[connected=" + connected + "]";
278         }
279     }
280 
281     private class CarrierServicePackageMonitor extends PackageMonitor {
282         @Override
onPackageAdded(String packageName, int reason)283         public void onPackageAdded(String packageName, int reason) {
284             evaluateBinding(packageName, true /* forceUnbind */);
285         }
286 
287         @Override
onPackageRemoved(String packageName, int reason)288         public void onPackageRemoved(String packageName, int reason) {
289             evaluateBinding(packageName, true /* forceUnbind */);
290         }
291 
292         @Override
onPackageUpdateFinished(String packageName, int uid)293         public void onPackageUpdateFinished(String packageName, int uid) {
294             evaluateBinding(packageName, true /* forceUnbind */);
295         }
296 
297         @Override
onPackageModified(String packageName)298         public void onPackageModified(String packageName) {
299             evaluateBinding(packageName, false /* forceUnbind */);
300         }
301 
302         @Override
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)303         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
304             if (doit) {
305                 for (String packageName : packages) {
306                     evaluateBinding(packageName, true /* forceUnbind */);
307                 }
308             }
309             return super.onHandleForceStop(intent, packages, uid, doit);
310         }
311 
evaluateBinding(String carrierPackageName, boolean forceUnbind)312         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
313             for (AppBinding appBinding : mBindings) {
314                 String appBindingPackage = appBinding.getPackage();
315                 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
316                 // Only log if this package was a carrier package to avoid log spam in the common
317                 // case that there are no carrier packages, but evaluate the binding if the package
318                 // is unset, in case this package change resulted in a new carrier package becoming
319                 // available for binding.
320                 if (isBindingForPackage) {
321                     log(carrierPackageName + " changed and corresponds to a phone. Rebinding.");
322                 }
323                 if (appBindingPackage == null || isBindingForPackage) {
324                     if (forceUnbind) {
325                         appBinding.unbind();
326                     }
327                     appBinding.rebind();
328                 }
329             }
330         }
331     }
332 
log(String message)333     private static void log(String message) {
334         Log.d(LOG_TAG, message);
335     }
336 
dump(FileDescriptor fd, PrintWriter pw, String[] args)337     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
338         pw.println("CarrierServiceBindHelper:");
339         for (AppBinding binding : mBindings) {
340             binding.dump(fd, pw, args);
341         }
342     }
343 }
344