• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.server.infra;
17 
18 import android.annotation.CallSuper;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.app.AppGlobals;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.pm.PackageItemInfo;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.ServiceInfo;
28 import android.graphics.drawable.Drawable;
29 import android.os.Process;
30 import android.os.RemoteException;
31 import android.os.UserManager;
32 import android.provider.Settings;
33 import android.text.TextUtils;
34 import android.util.Slog;
35 
36 import com.android.internal.annotations.GuardedBy;
37 
38 import java.io.PrintWriter;
39 
40 /**
41  * Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service
42  * implementation.
43  *
44  * @param <M> "main" service class.
45  * @param <S> "real" service class.
46  * @hide
47  */
48 public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
49         M extends AbstractMasterSystemService<M, S>> {
50 
51     @UserIdInt protected final int mUserId;
52     public final Object mLock;
53     protected final String mTag = getClass().getSimpleName();
54 
55     protected final M mMaster;
56 
57     /**
58      * Whether service was disabled for user due to {@link UserManager} restrictions.
59      */
60     @GuardedBy("mLock")
61     private boolean mDisabled;
62 
63     /**
64      * Caches whether the setup completed for the current user.
65      */
66     @GuardedBy("mLock")
67     private boolean mSetupComplete;
68 
69     @GuardedBy("mLock")
70     private ServiceInfo mServiceInfo;
71 
AbstractPerUserSystemService(@onNull M master, @NonNull Object lock, @UserIdInt int userId)72     protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
73             @UserIdInt int userId) {
74         mMaster = master;
75         mLock = lock;
76         mUserId = userId;
77         updateIsSetupComplete(userId);
78     }
79 
80     /** Updates whether setup is complete for current user */
updateIsSetupComplete(@serIdInt int userId)81     private void updateIsSetupComplete(@UserIdInt int userId) {
82         final String setupComplete = Settings.Secure.getStringForUser(
83                 getContext().getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, userId);
84         mSetupComplete = "1".equals(setupComplete);
85     }
86 
87     /**
88      * Creates a new {@link ServiceInfo} for the given service name.
89      *
90      * <p><b>MUST</b> be overridden by subclasses that bind to an
91      * {@link com.android.internal.infra.AbstractRemoteService}.
92      *
93      * @return new {@link ServiceInfo},
94      * @throws NameNotFoundException         if the service does not exist.
95      * @throws SecurityException             if the service does not have the proper permissions to
96      *                                       be bound to.
97      * @throws UnsupportedOperationException if subclass binds to a remote service but does not
98      *                                       overrides it.
99      */
newServiceInfoLocked( @uppressWarnings"unused") @onNull ComponentName serviceComponent)100     @NonNull protected ServiceInfo newServiceInfoLocked(
101             @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
102             throws NameNotFoundException {
103         throw new UnsupportedOperationException("not overridden");
104     }
105 
106     /**
107      * Callback called when an app has been updated.
108      *
109      * @param packageName package of the app being updated.
110      */
handlePackageUpdateLocked(@onNull String packageName)111     protected void handlePackageUpdateLocked(@NonNull String packageName) {
112     }
113 
114     /**
115      * Gets whether the service is enabled and ready.
116      */
117     @GuardedBy("mLock")
isEnabledLocked()118     protected boolean isEnabledLocked() {
119         return mSetupComplete && mServiceInfo != null && !mDisabled;
120     }
121 
122     /**
123      * Gets whether the service is disabled by {@link UserManager} restrictions.
124      */
isDisabledByUserRestrictionsLocked()125     protected final boolean isDisabledByUserRestrictionsLocked() {
126         return mDisabled;
127     }
128 
129     /**
130      * Updates the state of this service.
131      *
132      * <p>Typically called when the service {@link Settings} property or {@link UserManager}
133      * restriction changed, which includes the initial creation of the service.
134      *
135      * <p>Subclasses can extend this method to provide extra initialization, like clearing up
136      * previous state.
137      *
138      * @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
139      * @return whether the disabled state changed.
140      */
141     @GuardedBy("mLock")
142     @CallSuper
updateLocked(boolean disabled)143     protected boolean updateLocked(boolean disabled) {
144 
145         final boolean wasEnabled = isEnabledLocked();
146         if (mMaster.verbose) {
147             Slog.v(mTag, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
148                     + ", mSetupComplete=" + mSetupComplete
149                     + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
150         }
151 
152         updateIsSetupComplete(mUserId);
153         mDisabled = disabled;
154 
155         if (mMaster.mServiceNameResolver != null
156                 && mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
157             // Update of multi configured mode should always happen in AbstractMasterSystemService
158             // as this class is not aware of the complete list of multiple backends. Since we
159             // should never end up in this state, it is safe to not do anything if we end up here
160             // through a different code path.
161             if (mMaster.debug) {
162                 Slog.d(mTag, "Should not end up in updateLocked when "
163                         + "isConfiguredInMultipleMode is true");
164             }
165         } else {
166             updateServiceInfoLocked();
167         }
168         return wasEnabled != isEnabledLocked();
169     }
170 
171     /**
172      * Updates the internal reference to the service info, and returns the service's component.
173      */
174     @GuardedBy("mLock")
updateServiceInfoLocked()175     protected final ComponentName updateServiceInfoLocked() {
176         ComponentName[] componentNames = updateServiceInfoListLocked();
177         return componentNames == null || componentNames.length == 0 ? null : componentNames[0];
178     }
179 
180     /**
181      * Updates the internal reference to the service info, and returns the service's component.
182      */
183     @GuardedBy("mLock")
updateServiceInfoListLocked()184     protected final ComponentName[] updateServiceInfoListLocked() {
185         if (mMaster.mServiceNameResolver == null) {
186             return null;
187         }
188         if (!mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
189             final String componentName = getComponentNameLocked();
190             return new ComponentName[] { getServiceComponent(componentName) };
191         }
192 
193         final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
194                 mUserId);
195         if (componentNames == null) {
196             return null;
197         }
198         ComponentName[] serviceComponents = new ComponentName[componentNames.length];
199         for (int i = 0; i < componentNames.length; i++) {
200             serviceComponents[i] = getServiceComponent(componentNames[i]);
201         }
202         return serviceComponents;
203     }
204 
getServiceComponent(String componentName)205     private ComponentName getServiceComponent(String componentName) {
206         synchronized (mLock) {
207             ServiceInfo serviceInfo = null;
208             ComponentName serviceComponent = null;
209             if (!TextUtils.isEmpty(componentName)) {
210                 try {
211                     serviceComponent = ComponentName.unflattenFromString(componentName);
212                     serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
213                             0, mUserId);
214                     if (serviceInfo == null) {
215                         Slog.e(mTag, "Bad service name: " + componentName);
216                     }
217                 } catch (RuntimeException | RemoteException e) {
218                     Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
219                     serviceInfo = null;
220                 }
221             }
222             try {
223                 if (serviceInfo != null) {
224                     mServiceInfo = newServiceInfoLocked(serviceComponent);
225                     if (mMaster.debug) {
226                         Slog.d(mTag, "Set component for user " + mUserId + " as "
227                                 + serviceComponent + " and info as " + mServiceInfo);
228                     }
229                 } else {
230                     mServiceInfo = null;
231                     if (mMaster.debug) {
232                         Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
233                     }
234                 }
235             } catch (Exception e) {
236                 Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
237                 mServiceInfo = null;
238             }
239             return serviceComponent;
240         }
241     }
242 
243     /**
244      * Gets the user associated with this service.
245      */
getUserId()246     @UserIdInt public final int getUserId() {
247         return mUserId;
248     }
249 
250     /**
251      * Gets the main service.
252      */
getMaster()253     public final M getMaster() {
254         return mMaster;
255     }
256 
257     /**
258      * Gets this UID of the remote service this service binds to, or {@code -1} if the service is
259      * disabled.
260      */
261     @GuardedBy("mLock")
getServiceUidLocked()262     protected final int getServiceUidLocked() {
263         if (mServiceInfo == null) {
264             if (mMaster.verbose) Slog.v(mTag, "getServiceUidLocked(): no mServiceInfo");
265             return Process.INVALID_UID;
266         }
267         return mServiceInfo.applicationInfo.uid;
268     }
269 
270     /**
271      * Gets the current name of the service, which is either the default service or the
272      * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
273      */
274     @Nullable
275     @GuardedBy("mLock")
getComponentNameLocked()276     protected final String getComponentNameLocked() {
277         return mMaster.mServiceNameResolver.getServiceName(mUserId);
278     }
279 
280     /**
281      * Gets the current name of the service, which is either the default service or the
282      * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
283      */
284     @Nullable
285     @GuardedBy("mLock")
getComponentNameForMultipleLocked(String serviceName)286     protected final String getComponentNameForMultipleLocked(String serviceName) {
287         String[] services = mMaster.mServiceNameResolver.getServiceNameList(mUserId);
288         for (int i = 0; i < services.length; i++) {
289             if (serviceName.equals(services[i])) {
290                 return services[i];
291             }
292         }
293         return null;
294     }
295 
296     /**
297      * Checks whether the current service for the user was temporarily set.
298      */
299     @GuardedBy("mLock")
isTemporaryServiceSetLocked()300     public final boolean isTemporaryServiceSetLocked() {
301         return mMaster.mServiceNameResolver.isTemporary(mUserId);
302     }
303 
304     /**
305      * Resets the temporary service implementation to the default component.
306      */
307     @GuardedBy("mLock")
resetTemporaryServiceLocked()308     protected final void resetTemporaryServiceLocked() {
309         mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
310     }
311 
312     /**
313      * Gets the {@link ServiceInfo} of the remote service this service binds to, or {@code null}
314      * if the service is disabled.
315      */
316     @Nullable
getServiceInfo()317     public final ServiceInfo getServiceInfo() {
318         return mServiceInfo;
319     }
320 
321     /**
322      * Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
323      * if the service is disabled.
324      */
325     @Nullable
getServiceComponentName()326     public final ComponentName getServiceComponentName() {
327         synchronized (mLock) {
328             return mServiceInfo == null ? null : mServiceInfo.getComponentName();
329         }
330     }
331 
332     /**
333      * Gets the name of the of the app this service binds to, or {@code null} if the service is
334      * disabled.
335      */
336     @Nullable
getServicePackageName()337     public final String getServicePackageName() {
338         final ComponentName serviceComponent = getServiceComponentName();
339         return serviceComponent == null ? null : serviceComponent.getPackageName();
340     }
341 
342     /**
343      * Gets the user-visibile name of the service this service binds to, or {@code null} if the
344      * service is disabled.
345      */
346     @Nullable
347     @GuardedBy("mLock")
getServiceLabelLocked()348     public final CharSequence getServiceLabelLocked() {
349         return mServiceInfo == null ? null : mServiceInfo.loadSafeLabel(
350                 getContext().getPackageManager(), 0 /* do not ellipsize */,
351                 PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
352     }
353 
354     /**
355      * Gets the icon the service this service binds to, or {@code null} if the service is disabled.
356      */
357     @Nullable
358     @GuardedBy("mLock")
getServiceIconLocked()359     public final Drawable getServiceIconLocked() {
360         return mServiceInfo == null ? null
361                 : mServiceInfo.loadIcon(getContext().getPackageManager());
362     }
363 
364     /**
365      * Removes the service from the main service's cache.
366      */
removeSelfFromCache()367     protected final void removeSelfFromCache() {
368         synchronized (mMaster.mLock) {
369             mMaster.removeCachedServiceListLocked(mUserId);
370         }
371     }
372 
373     /**
374      * Whether the service should log debug statements.
375      */
376     //TODO(b/117779333): consider using constants for these guards
isDebug()377     public final boolean isDebug() {
378         return mMaster.debug;
379     }
380 
381     /**
382      * Whether the service should log verbose statements.
383      */
384     //TODO(b/117779333): consider using constants for these guards
isVerbose()385     public final boolean isVerbose() {
386         return mMaster.verbose;
387     }
388 
389     /**
390      * Gets the target SDK level of the service this service binds to,
391      * or {@code 0} if the service is disabled.
392      */
393     @GuardedBy("mLock")
getTargedSdkLocked()394     public final int getTargedSdkLocked() {
395         return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
396     }
397 
398     /**
399      * Gets whether the device already finished setup.
400      */
401     @GuardedBy("mLock")
isSetupCompletedLocked()402     protected final boolean isSetupCompletedLocked() {
403         return mSetupComplete;
404     }
405 
406     /**
407      * Gets the context associated with this service.
408      */
getContext()409     protected final Context getContext() {
410         return mMaster.getContext();
411     }
412 
413     // TODO(b/117779333): support proto
414     @GuardedBy("mLock")
dumpLocked(@onNull String prefix, @NonNull PrintWriter pw)415     protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
416         pw.print(prefix);
417         pw.print("User: ");
418         pw.println(mUserId);
419         if (mServiceInfo != null) {
420             pw.print(prefix);
421             pw.print("Service Label: ");
422             pw.println(getServiceLabelLocked());
423             pw.print(prefix);
424             pw.print("Target SDK: ");
425             pw.println(getTargedSdkLocked());
426         }
427         if (mMaster.mServiceNameResolver != null) {
428             pw.print(prefix);
429             pw.print("Name resolver: ");
430             mMaster.mServiceNameResolver.dumpShort(pw, mUserId);
431             pw.println();
432         }
433         pw.print(prefix);
434         pw.print("Disabled by UserManager: ");
435         pw.println(mDisabled);
436         pw.print(prefix);
437         pw.print("Setup complete: ");
438         pw.println(mSetupComplete);
439         if (mServiceInfo != null) {
440             pw.print(prefix);
441             pw.print("Service UID: ");
442             pw.println(mServiceInfo.applicationInfo.uid);
443         }
444         pw.println();
445     }
446 }
447