• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.net.wifi;
18 
19 import android.annotation.NonNull;
20 import android.app.ActivityManager;
21 import android.content.Context;
22 import android.content.ContextWrapper;
23 import android.content.Intent;
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.net.wifi.util.Environment;
29 import android.os.UserHandle;
30 import android.util.Log;
31 
32 import androidx.annotation.Nullable;
33 
34 import java.util.List;
35 import java.util.stream.Collectors;
36 
37 /**
38  * Wrapper for context to override getResources method. Resources for wifi mainline jar needs to be
39  * fetched from the resources APK.
40  *
41  * @hide
42  */
43 public class WifiContext extends ContextWrapper {
44     private static final String TAG = "WifiContext";
45     /** Intent action that is used to identify ServiceWifiResources.apk */
46     private static final String ACTION_RESOURCES_APK =
47             "com.android.server.wifi.intent.action.SERVICE_WIFI_RESOURCES_APK";
48     /** Intent action that is used to identify WifiDialog.apk */
49     private static final String ACTION_WIFI_DIALOG_APK =
50             "com.android.server.wifi.intent.action.WIFI_DIALOG_APK";
51 
52     /** Since service-wifi runs within system_server, its package name is "android". */
53     private static final String SERVICE_WIFI_PACKAGE_NAME = "android";
54 
55     private String mWifiOverlayApkPkgName;
56     private String mWifiDialogApkPkgName;
57 
58     // Cached resources from the resources APK.
59     private AssetManager mWifiAssetsFromApk;
60     private Resources mWifiResourcesFromApk;
61     private Resources.Theme mWifiThemeFromApk;
62 
WifiContext(@onNull Context contextBase)63     public WifiContext(@NonNull Context contextBase) {
64         super(contextBase);
65     }
66 
67     /** Get the package name of ServiceWifiResources.apk */
getWifiOverlayApkPkgName()68     public @Nullable String getWifiOverlayApkPkgName() {
69         if (mWifiOverlayApkPkgName != null) {
70             return mWifiOverlayApkPkgName;
71         }
72         mWifiOverlayApkPkgName = getApkPkgNameForAction(ACTION_RESOURCES_APK, null);
73         if (mWifiOverlayApkPkgName == null) {
74             // Resource APK not loaded yet, print a stack trace to see where this is called from
75             Log.e(TAG, "Attempted to fetch resources before Wifi Resources APK is loaded!",
76                     new IllegalStateException());
77             return null;
78         }
79         Log.i(TAG, "Found Wifi Resources APK at: " + mWifiOverlayApkPkgName);
80         return mWifiOverlayApkPkgName;
81     }
82 
83     /** Get the package name of WifiDialog.apk */
getWifiDialogApkPkgName()84     public @Nullable String getWifiDialogApkPkgName() {
85         if (mWifiDialogApkPkgName != null) {
86             return mWifiDialogApkPkgName;
87         }
88         mWifiDialogApkPkgName = getApkPkgNameForAction(ACTION_WIFI_DIALOG_APK,
89                 UserHandle.of(ActivityManager.getCurrentUser()));
90         if (mWifiDialogApkPkgName == null) {
91             // WifiDialog APK not loaded yet, print a stack trace to see where this is called from
92             Log.e(TAG, "Attempted to fetch WifiDialog apk before it is loaded!",
93                     new IllegalStateException());
94             return null;
95         }
96         Log.i(TAG, "Found Wifi Dialog APK at: " + mWifiDialogApkPkgName);
97         return mWifiDialogApkPkgName;
98     }
99 
100     /** Gets the package name of the apk responding to the given intent action */
getApkPkgNameForAction(@onNull String action, UserHandle userHandle)101     private String getApkPkgNameForAction(@NonNull String action, UserHandle userHandle) {
102 
103         List<ResolveInfo> resolveInfos;
104         if (userHandle != null) {
105             resolveInfos = getPackageManager().queryIntentActivitiesAsUser(
106                     new Intent(action),
107                     PackageManager.MATCH_SYSTEM_ONLY,
108                     userHandle);
109         } else {
110             resolveInfos = getPackageManager().queryIntentActivities(
111                     new Intent(action),
112                     PackageManager.MATCH_SYSTEM_ONLY);
113         }
114         Log.i(TAG, "Got resolveInfos for " + action + ": " + resolveInfos);
115 
116         // remove apps that don't live in the Wifi apex
117         resolveInfos.removeIf(info ->
118                 !Environment.isAppInWifiApex(info.activityInfo.applicationInfo));
119 
120         if (resolveInfos.isEmpty()) {
121             return null;
122         }
123 
124         if (resolveInfos.size() > 1) {
125             // multiple apps found, log a warning, but continue
126             Log.w(TAG, "Found > 1 APK that can resolve " + action + ": "
127                     + resolveInfos.stream()
128                     .map(info -> info.activityInfo.applicationInfo.packageName)
129                     .collect(Collectors.joining(", ")));
130         }
131 
132         // Assume the first ResolveInfo is the one we're looking for
133         ResolveInfo info = resolveInfos.get(0);
134         return info.activityInfo.applicationInfo.packageName;
135     }
136 
getResourcesApkContext()137     private Context getResourcesApkContext() {
138         try {
139             String packageName = getWifiOverlayApkPkgName();
140             if (packageName != null) {
141                 return createPackageContext(packageName, 0);
142             }
143         } catch (PackageManager.NameNotFoundException e) {
144             Log.wtf(TAG, "Failed to load resources", e);
145         }
146         return null;
147     }
148 
149     /**
150      * Retrieve assets held in the wifi resources APK.
151      */
152     @Override
getAssets()153     public AssetManager getAssets() {
154         if (mWifiAssetsFromApk == null) {
155             Context resourcesApkContext = getResourcesApkContext();
156             if (resourcesApkContext != null) {
157                 mWifiAssetsFromApk = resourcesApkContext.getAssets();
158             }
159         }
160         return mWifiAssetsFromApk;
161     }
162 
163     /**
164      * Retrieve resources held in the wifi resources APK.
165      */
166     @Override
getResources()167     public Resources getResources() {
168         if (mWifiResourcesFromApk == null) {
169             Context resourcesApkContext = getResourcesApkContext();
170             if (resourcesApkContext != null) {
171                 mWifiResourcesFromApk = resourcesApkContext.getResources();
172             }
173         }
174         return mWifiResourcesFromApk;
175     }
176 
177     /**
178      * Retrieve theme held in the wifi resources APK.
179      */
180     @Override
getTheme()181     public Resources.Theme getTheme() {
182         if (mWifiThemeFromApk == null) {
183             Context resourcesApkContext = getResourcesApkContext();
184             if (resourcesApkContext != null) {
185                 mWifiThemeFromApk = resourcesApkContext.getTheme();
186             }
187         }
188         return mWifiThemeFromApk;
189     }
190 
191     /** Get the package name that service-wifi runs under. */
getServiceWifiPackageName()192     public String getServiceWifiPackageName() {
193         return SERVICE_WIFI_PACKAGE_NAME;
194     }
195 
196     /**
197      * Reset the resource cache which will cause it to be reloaded next time it is accessed.
198      */
resetResourceCache()199     public void resetResourceCache() {
200         mWifiOverlayApkPkgName = null;
201         mWifiAssetsFromApk = null;
202         mWifiResourcesFromApk = null;
203         mWifiThemeFromApk = null;
204     }
205 
206     /**
207      * Returns an instance of WifiStringResourceWrapper with the given subId and carrierId.
208      */
getStringResourceWrapper(int subId, int carrierId)209     public WifiStringResourceWrapper getStringResourceWrapper(int subId, int carrierId) {
210         return new WifiStringResourceWrapper(this, subId, carrierId);
211     }
212 }
213