1 /*
2  * Copyright (C) 2019 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.wifi;
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.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.content.res.AssetManager;
26 import android.content.res.Resources;
27 import android.util.Log;
28 
29 import com.android.server.wifi.util.Environment;
30 
31 import java.util.List;
32 import java.util.stream.Collectors;
33 
34 /**
35  * Wrapper for context to override getResources method. Resources for wifi mainline jar needs to be
36  * fetched from the resources APK.
37  */
38 public class WifiContext extends ContextWrapper {
39     private static final String TAG = "WifiContext";
40     /** Intent action that is used to identify ServiceWifiResources.apk */
41     private static final String ACTION_RESOURCES_APK =
42             "com.android.server.wifi.intent.action.SERVICE_WIFI_RESOURCES_APK";
43     private static final String WIFI_OVERLAY_JAVA_PKG_NAME = "com.android.wifi.resources";
44 
45     private String mWifiOverlayApkPkgName;
46 
47     // Cached resources from the resources APK.
48     private AssetManager mWifiAssetsFromApk;
49     private Resources mWifiResourcesFromApk;
50     private Resources.Theme mWifiThemeFromApk;
51 
WifiContext(@onNull Context contextBase)52     public WifiContext(@NonNull Context contextBase) {
53         super(contextBase);
54     }
55 
56     /**
57      * Get the Java package name of the resources in ServiceWifiResources.apk
58      *
59      * i.e. the package name of the Wifi Resources R class:
60      * {@code import com.android.wifi.resources.R;}, which is "com.android.wifi.resources"
61      */
getWifiOverlayJavaPkgName()62     public String getWifiOverlayJavaPkgName() {
63         return WIFI_OVERLAY_JAVA_PKG_NAME;
64     }
65 
66     /** Get the Android application package name of ServiceWifiResources.apk */
getWifiOverlayApkPkgName()67     public String getWifiOverlayApkPkgName() {
68         if (mWifiOverlayApkPkgName != null) {
69             return mWifiOverlayApkPkgName;
70         }
71 
72         List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(
73                 new Intent(ACTION_RESOURCES_APK),
74                 PackageManager.MATCH_SYSTEM_ONLY);
75 
76         // remove apps that don't live in the Wifi apex
77         resolveInfos.removeIf(info ->
78                 !Environment.isAppInWifiApex(info.activityInfo.applicationInfo));
79 
80         if (resolveInfos.isEmpty()) {
81             // Resource APK not loaded yet, print a stack trace to see where this is called from
82             Log.e(TAG, "Attempted to fetch resources before Wifi Resources APK is loaded!",
83                     new IllegalStateException());
84             return null;
85         }
86 
87         if (resolveInfos.size() > 1) {
88             // multiple apps found, log a warning, but continue
89             Log.w(TAG, "Found > 1 APK that can resolve Wifi Resources APK intent: "
90                     + resolveInfos.stream()
91                             .map(info -> info.activityInfo.applicationInfo.packageName)
92                             .collect(Collectors.joining(", ")));
93         }
94 
95         // Assume the first ResolveInfo is the one we're looking for
96         ResolveInfo info = resolveInfos.get(0);
97         mWifiOverlayApkPkgName = info.activityInfo.applicationInfo.packageName;
98         Log.i(TAG, "Found Wifi Resources APK at: " + mWifiOverlayApkPkgName);
99         return mWifiOverlayApkPkgName;
100     }
101 
getResourcesApkContext()102     private Context getResourcesApkContext() {
103         try {
104             return createPackageContext(getWifiOverlayApkPkgName(), 0);
105         } catch (PackageManager.NameNotFoundException e) {
106             Log.wtf(TAG, "Failed to load resources", e);
107         }
108         return null;
109     }
110 
111     /**
112      * Retrieve assets held in the wifi resources APK.
113      */
114     @Override
getAssets()115     public AssetManager getAssets() {
116         if (mWifiAssetsFromApk == null) {
117             Context resourcesApkContext = getResourcesApkContext();
118             if (resourcesApkContext != null) {
119                 mWifiAssetsFromApk = resourcesApkContext.getAssets();
120             }
121         }
122         return mWifiAssetsFromApk;
123     }
124 
125     /**
126      * Retrieve resources held in the wifi resources APK.
127      */
128     @Override
getResources()129     public Resources getResources() {
130         if (mWifiResourcesFromApk == null) {
131             Context resourcesApkContext = getResourcesApkContext();
132             if (resourcesApkContext != null) {
133                 mWifiResourcesFromApk = resourcesApkContext.getResources();
134             }
135         }
136         return mWifiResourcesFromApk;
137     }
138 
139     /**
140      * Retrieve theme held in the wifi resources APK.
141      */
142     @Override
getTheme()143     public Resources.Theme getTheme() {
144         if (mWifiThemeFromApk == null) {
145             Context resourcesApkContext = getResourcesApkContext();
146             if (resourcesApkContext != null) {
147                 mWifiThemeFromApk = resourcesApkContext.getTheme();
148             }
149         }
150         return mWifiThemeFromApk;
151     }
152 }
153