1 /*
2  * Copyright (C) 2015 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.internal.app;
18 
19 import com.android.internal.R;
20 
21 import android.app.SearchManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.provider.Settings;
33 import android.util.Log;
34 
35 /**
36  * Utility method for dealing with the assistant aspects of
37  * {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
38  */
39 public class AssistUtils {
40 
41     private static final String TAG = "AssistUtils";
42 
43     private final Context mContext;
44     private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
45 
AssistUtils(Context context)46     public AssistUtils(Context context) {
47         mContext = context;
48         mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
49                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
50     }
51 
showSessionForActiveService(Bundle args, int sourceFlags, IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken)52     public boolean showSessionForActiveService(Bundle args, int sourceFlags,
53             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
54         try {
55             if (mVoiceInteractionManagerService != null) {
56                 return mVoiceInteractionManagerService.showSessionForActiveService(args,
57                         sourceFlags, showCallback, activityToken);
58             }
59         } catch (RemoteException e) {
60             Log.w(TAG, "Failed to call showSessionForActiveService", e);
61         }
62         return false;
63     }
64 
launchVoiceAssistFromKeyguard()65     public void launchVoiceAssistFromKeyguard() {
66         try {
67             if (mVoiceInteractionManagerService != null) {
68                 mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard();
69             }
70         } catch (RemoteException e) {
71             Log.w(TAG, "Failed to call launchVoiceAssistFromKeyguard", e);
72         }
73     }
74 
activeServiceSupportsAssistGesture()75     public boolean activeServiceSupportsAssistGesture() {
76         try {
77             return mVoiceInteractionManagerService != null
78                     && mVoiceInteractionManagerService.activeServiceSupportsAssist();
79         } catch (RemoteException e) {
80             Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e);
81             return false;
82         }
83     }
84 
activeServiceSupportsLaunchFromKeyguard()85     public boolean activeServiceSupportsLaunchFromKeyguard() {
86         try {
87             return mVoiceInteractionManagerService != null
88                     && mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard();
89         } catch (RemoteException e) {
90             Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e);
91             return false;
92         }
93     }
94 
getActiveServiceComponentName()95     public ComponentName getActiveServiceComponentName() {
96         try {
97             if (mVoiceInteractionManagerService != null) {
98                 return mVoiceInteractionManagerService.getActiveServiceComponentName();
99             } else {
100                 return null;
101             }
102         } catch (RemoteException e) {
103             Log.w(TAG, "Failed to call getActiveServiceComponentName", e);
104             return null;
105         }
106     }
107 
isSessionRunning()108     public boolean isSessionRunning() {
109         try {
110             return mVoiceInteractionManagerService != null
111                     && mVoiceInteractionManagerService.isSessionRunning();
112         } catch (RemoteException e) {
113             Log.w(TAG, "Failed to call isSessionRunning", e);
114             return false;
115         }
116     }
117 
hideCurrentSession()118     public void hideCurrentSession() {
119         try {
120             if (mVoiceInteractionManagerService != null) {
121                 mVoiceInteractionManagerService.hideCurrentSession();
122             }
123         } catch (RemoteException e) {
124             Log.w(TAG, "Failed to call hideCurrentSession", e);
125         }
126     }
127 
onLockscreenShown()128     public void onLockscreenShown() {
129         try {
130             if (mVoiceInteractionManagerService != null) {
131                 mVoiceInteractionManagerService.onLockscreenShown();
132             }
133         } catch (RemoteException e) {
134             Log.w(TAG, "Failed to call onLockscreenShown", e);
135         }
136     }
137 
registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener)138     public void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener) {
139         try {
140             if (mVoiceInteractionManagerService != null) {
141                 mVoiceInteractionManagerService.registerVoiceInteractionSessionListener(listener);
142             }
143         } catch (RemoteException e) {
144             Log.w(TAG, "Failed to register voice interaction listener", e);
145         }
146     }
147 
getAssistComponentForUser(int userId)148     public ComponentName getAssistComponentForUser(int userId) {
149         final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
150                 Settings.Secure.ASSISTANT, userId);
151         if (setting != null) {
152             return ComponentName.unflattenFromString(setting);
153         }
154 
155         // Fallback to keep backward compatible behavior when there is no user setting.
156         if (activeServiceSupportsAssistGesture()) {
157             return getActiveServiceComponentName();
158         }
159         final SearchManager searchManager =
160             (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
161         if (searchManager == null) {
162             return null;
163         }
164         final Intent intent = searchManager.getAssistIntent(false);
165         PackageManager pm = mContext.getPackageManager();
166         ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY,
167                 userId);
168         if (info != null) {
169             return new ComponentName(info.activityInfo.applicationInfo.packageName,
170                     info.activityInfo.name);
171         }
172         return null;
173     }
174 
isPreinstalledAssistant(Context context, ComponentName assistant)175     public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) {
176         if (assistant == null) {
177             return false;
178         }
179         ApplicationInfo applicationInfo;
180         try {
181             applicationInfo = context.getPackageManager().getApplicationInfo(
182                     assistant.getPackageName(), 0);
183         } catch (PackageManager.NameNotFoundException e) {
184             return false;
185         }
186         return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp();
187     }
188 
isDisclosureEnabled(Context context)189     private static boolean isDisclosureEnabled(Context context) {
190         return Settings.Secure.getInt(context.getContentResolver(),
191                 Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0;
192     }
193 
194     /**
195      * @return if the disclosure animation should trigger for the given assistant.
196      *
197      * Third-party assistants will always need to disclose, while the user can configure this for
198      * pre-installed assistants.
199      */
shouldDisclose(Context context, ComponentName assistant)200     public static boolean shouldDisclose(Context context, ComponentName assistant) {
201         if (!allowDisablingAssistDisclosure(context)) {
202             return true;
203         }
204 
205         return isDisclosureEnabled(context) || !isPreinstalledAssistant(context, assistant);
206     }
207 
allowDisablingAssistDisclosure(Context context)208     public static boolean allowDisablingAssistDisclosure(Context context) {
209         return context.getResources().getBoolean(
210                 com.android.internal.R.bool.config_allowDisablingAssistDisclosure);
211     }
212 }
213