• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.uwb;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.ContextWrapper;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.content.res.AssetManager;
27 import android.content.res.Resources;
28 import android.util.Log;
29 
30 import java.io.File;
31 import java.util.List;
32 import java.util.stream.Collectors;
33 
34 /**
35  * Wrapper for context to override getResources method. Resources for uwb mainline jar needs to be
36  * fetched from the resources APK.
37  */
38 public class UwbContext extends ContextWrapper {
39     private static final String TAG = "UwbContext";
40     /** Intent action that is used to identify ServiceUwbResources.apk */
41     private static final String ACTION_RESOURCES_APK =
42             "com.android.server.uwb.intent.action.SERVICE_UWB_RESOURCES_APK";
43 
44     /** Since service-uwb runs within system_server, its package name is "android". */
45     private static final String SERVICE_UWB_PACKAGE_NAME = "android";
46     private static final String UWB_APEX_NAME = "com.android.uwb";
47     /**
48      * The path where the Uwb apex is mounted.
49      * Current value = "/apex/com.android.uwb"
50      */
51     private static final String UWB_APEX_PATH =
52             new File("/apex", UWB_APEX_NAME).getAbsolutePath();
53 
54     private String mUwbOverlayApkPkgName;
55 
56     // Cached resources from the resources APK.
57     private AssetManager mUwbAssetsFromApk;
58     private Resources mUwbResourcesFromApk;
59     private Resources.Theme mUwbThemeFromApk;
60 
UwbContext(@onNull Context contextBase)61     public UwbContext(@NonNull Context contextBase) {
62         super(contextBase);
63     }
64 
65     /**
66      * Returns true if the app is in the Uwb apex, false otherwise.
67      * Checks if the app's path starts with "/apex/com.android.uwb".
68      */
isAppInUwbApex(ApplicationInfo appInfo)69     private static boolean isAppInUwbApex(ApplicationInfo appInfo) {
70         return appInfo.sourceDir.startsWith(UWB_APEX_PATH);
71     }
72 
73     /** Get the package name of ServiceUwbResources.apk */
getUwbOverlayApkPkgName()74     public String getUwbOverlayApkPkgName() {
75         if (mUwbOverlayApkPkgName != null) {
76             return mUwbOverlayApkPkgName;
77         }
78 
79         List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(
80                 new Intent(ACTION_RESOURCES_APK),
81                 PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DISABLED_COMPONENTS);
82 
83         // remove apps that don't live in the Uwb apex
84         resolveInfos.removeIf(info ->
85                 !isAppInUwbApex(info.activityInfo.applicationInfo));
86 
87         if (resolveInfos.isEmpty()) {
88             // Resource APK not loaded yet, print a stack trace to see where this is called from
89             Log.e(TAG, "Attempted to fetch resources before Uwb Resources APK is loaded!",
90                     new IllegalStateException());
91             return null;
92         }
93 
94         if (resolveInfos.size() > 1) {
95             // multiple apps found, log a warning, but continue
96             Log.w(TAG, "Found > 1 APK that can resolve Uwb Resources APK intent: "
97                     + resolveInfos.stream()
98                             .map(info -> info.activityInfo.applicationInfo.packageName)
99                             .collect(Collectors.joining(", ")));
100         }
101 
102         // Assume the first ResolveInfo is the one we're looking for
103         ResolveInfo info = resolveInfos.get(0);
104         mUwbOverlayApkPkgName = info.activityInfo.applicationInfo.packageName;
105         Log.i(TAG, "Found Uwb Resources APK at: " + mUwbOverlayApkPkgName);
106         int enabledState = getPackageManager().getApplicationEnabledSetting(mUwbOverlayApkPkgName);
107         if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
108             Log.w(TAG, "Uwb resources APK disabled, state: " + enabledState
109                     + ". Trying to re-enable");
110             getPackageManager().setApplicationEnabledSetting(
111                     mUwbOverlayApkPkgName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
112         }
113         return mUwbOverlayApkPkgName;
114     }
115 
getResourcesApkContext()116     private Context getResourcesApkContext() {
117         try {
118             return createPackageContext(getUwbOverlayApkPkgName(), 0);
119         } catch (PackageManager.NameNotFoundException e) {
120             Log.wtf(TAG, "Failed to load resources", e);
121         }
122         return null;
123     }
124 
125     /**
126      * Retrieve assets held in the uwb resources APK.
127      */
128     @Override
getAssets()129     public AssetManager getAssets() {
130         if (mUwbAssetsFromApk == null) {
131             Context resourcesApkContext = getResourcesApkContext();
132             if (resourcesApkContext != null) {
133                 mUwbAssetsFromApk = resourcesApkContext.getAssets();
134             }
135         }
136         return mUwbAssetsFromApk;
137     }
138 
139     /**
140      * Retrieve resources held in the uwb resources APK.
141      */
142     @Override
getResources()143     public Resources getResources() {
144         if (mUwbResourcesFromApk == null) {
145             Context resourcesApkContext = getResourcesApkContext();
146             if (resourcesApkContext != null) {
147                 mUwbResourcesFromApk = resourcesApkContext.getResources();
148             }
149         }
150         return mUwbResourcesFromApk;
151     }
152 
153     /**
154      * Retrieve theme held in the uwb resources APK.
155      */
156     @Override
getTheme()157     public Resources.Theme getTheme() {
158         if (mUwbThemeFromApk == null) {
159             Context resourcesApkContext = getResourcesApkContext();
160             if (resourcesApkContext != null) {
161                 mUwbThemeFromApk = resourcesApkContext.getTheme();
162             }
163         }
164         return mUwbThemeFromApk;
165     }
166 
167     /** Get the package name that service-uwb runs under. */
getServiceUwbPackageName()168     public String getServiceUwbPackageName() {
169         return SERVICE_UWB_PACKAGE_NAME;
170     }
171 }
172