• 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.nearby.fastpair.halfsheet;
18 
19 import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
20 import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER;
21 import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE;
22 import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
23 import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE;
24 import static com.android.server.nearby.fastpair.FastPairManager.ACTION_RESOURCES_APK;
25 
26 import android.bluetooth.BluetoothDevice;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.nearby.FastPairDevice;
33 import android.nearby.FastPairStatusCallback;
34 import android.nearby.PairStatusMetadata;
35 import android.os.Bundle;
36 import android.os.UserHandle;
37 import android.util.Log;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.server.nearby.common.locator.LocatorContextWrapper;
41 import com.android.server.nearby.fastpair.FastPairController;
42 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
43 import com.android.server.nearby.util.Environment;
44 
45 import java.util.List;
46 import java.util.stream.Collectors;
47 
48 import service.proto.Cache;
49 
50 /**
51  * Fast Pair ux manager for half sheet.
52  */
53 public class FastPairHalfSheetManager {
54     private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
55     private static final String HALF_SHEET_CLASS_NAME =
56             "com.android.nearby.halfsheet.HalfSheetActivity";
57     private static final String TAG = "FPHalfSheetManager";
58 
59     private String mHalfSheetApkPkgName;
60     private final LocatorContextWrapper mLocatorContextWrapper;
61 
62     FastPairUiServiceImpl mFastPairUiService;
63 
FastPairHalfSheetManager(Context context)64     public FastPairHalfSheetManager(Context context) {
65         this(new LocatorContextWrapper(context));
66     }
67 
68     @VisibleForTesting
FastPairHalfSheetManager(LocatorContextWrapper locatorContextWrapper)69     FastPairHalfSheetManager(LocatorContextWrapper locatorContextWrapper) {
70         mLocatorContextWrapper = locatorContextWrapper;
71         mFastPairUiService = new FastPairUiServiceImpl();
72     }
73 
74     /**
75      * Invokes half sheet in the other apk. This function can only be called in Nearby because other
76      * app can't get the correct component name.
77      */
showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem)78     public void showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem) {
79         try {
80             if (mLocatorContextWrapper != null) {
81                 String packageName = getHalfSheetApkPkgName();
82                 if (packageName == null) {
83                     Log.e(TAG, "package name is null");
84                     return;
85                 }
86                 mFastPairUiService.setFastPairController(
87                         mLocatorContextWrapper.getLocator().get(FastPairController.class));
88                 Bundle bundle = new Bundle();
89                 bundle.putBinder(EXTRA_BINDER, mFastPairUiService);
90                 mLocatorContextWrapper
91                         .startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION)
92                                         .putExtra(EXTRA_HALF_SHEET_INFO,
93                                                 scanFastPairStoreItem.toByteArray())
94                                         .putExtra(EXTRA_HALF_SHEET_TYPE,
95                                                 DEVICE_PAIRING_FRAGMENT_TYPE)
96                                         .putExtra(EXTRA_BUNDLE, bundle)
97                                         .setComponent(new ComponentName(packageName,
98                                                 HALF_SHEET_CLASS_NAME)),
99                                 UserHandle.CURRENT);
100             }
101         } catch (IllegalStateException e) {
102             Log.e(TAG, "Can't resolve package that contains half sheet");
103         }
104     }
105 
106     /**
107      * Shows pairing fail half sheet.
108      */
showPairingFailed()109     public void showPairingFailed() {
110         FastPairStatusCallback pairStatusCallback = mFastPairUiService.getPairStatusCallback();
111         if (pairStatusCallback != null) {
112             Log.v(TAG, "showPairingFailed: pairStatusCallback not NULL");
113             pairStatusCallback.onPairUpdate(new FastPairDevice.Builder().build(),
114                     new PairStatusMetadata(PairStatusMetadata.Status.FAIL));
115         } else {
116             Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because "
117                     + "the pairStatusCallback is null");
118         }
119     }
120 
121     /**
122      * Get the half sheet status whether it is foreground or dismissed
123      */
getHalfSheetForegroundState()124     public boolean getHalfSheetForegroundState() {
125         return true;
126     }
127 
128     /**
129      * Show passkey confirmation info on half sheet
130      */
showPasskeyConfirmation(BluetoothDevice device, int passkey)131     public void showPasskeyConfirmation(BluetoothDevice device, int passkey) {
132     }
133 
134     /**
135      * This function will handle pairing steps for half sheet.
136      */
showPairingHalfSheet(DiscoveryItem item)137     public void showPairingHalfSheet(DiscoveryItem item) {
138         Log.d(TAG, "show pairing half sheet");
139     }
140 
141     /**
142      * Shows pairing success info.
143      */
showPairingSuccessHalfSheet(String address)144     public void showPairingSuccessHalfSheet(String address) {
145         FastPairStatusCallback pairStatusCallback = mFastPairUiService.getPairStatusCallback();
146         if (pairStatusCallback != null) {
147             pairStatusCallback.onPairUpdate(
148                     new FastPairDevice.Builder().setBluetoothAddress(address).build(),
149                     new PairStatusMetadata(PairStatusMetadata.Status.SUCCESS));
150         } else {
151             Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because "
152                     + "the pairStatusCallback is null");
153         }
154     }
155 
156     /**
157      * Removes dismiss runnable.
158      */
disableDismissRunnable()159     public void disableDismissRunnable() {
160     }
161 
162     /**
163      * Destroys the bluetooth pairing controller.
164      */
destroyBluetoothPairController()165     public void destroyBluetoothPairController() {
166     }
167 
168     /**
169      * Notify manager the pairing has finished.
170      */
notifyPairingProcessDone(boolean success, String address, DiscoveryItem item)171     public void notifyPairingProcessDone(boolean success, String address, DiscoveryItem item) {
172     }
173 
174     /**
175      * Gets the package name of HalfSheet.apk
176      * getHalfSheetApkPkgName may invoke PackageManager multiple times and it does not have
177      * race condition check. Since there is no lock for mHalfSheetApkPkgName.
178      */
getHalfSheetApkPkgName()179     String getHalfSheetApkPkgName() {
180         if (mHalfSheetApkPkgName != null) {
181             return mHalfSheetApkPkgName;
182         }
183         List<ResolveInfo> resolveInfos = mLocatorContextWrapper
184                 .getPackageManager().queryIntentActivities(
185                         new Intent(ACTION_RESOURCES_APK),
186                         PackageManager.MATCH_SYSTEM_ONLY);
187 
188         // remove apps that don't live in the nearby apex
189         resolveInfos.removeIf(info ->
190                 !Environment.isAppInNearbyApex(info.activityInfo.applicationInfo));
191 
192         if (resolveInfos.isEmpty()) {
193             // Resource APK not loaded yet, print a stack trace to see where this is called from
194             Log.e("FastPairManager", "Attempted to fetch resources before halfsheet "
195                             + " APK is installed or package manager can't resolve correctly!",
196                     new IllegalStateException());
197             return null;
198         }
199 
200         if (resolveInfos.size() > 1) {
201             // multiple apps found, log a warning, but continue
202             Log.w("FastPairManager", "Found > 1 APK that can resolve halfsheet APK intent: "
203                     + resolveInfos.stream()
204                     .map(info -> info.activityInfo.applicationInfo.packageName)
205                     .collect(Collectors.joining(", ")));
206         }
207 
208         // Assume the first ResolveInfo is the one we're looking for
209         ResolveInfo info = resolveInfos.get(0);
210         mHalfSheetApkPkgName = info.activityInfo.applicationInfo.packageName;
211         Log.i("FastPairManager", "Found halfsheet APK at: " + mHalfSheetApkPkgName);
212         return mHalfSheetApkPkgName;
213     }
214 }
215