• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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.bluetooth;
18 
19 import static com.android.server.bluetooth.BluetoothAirplaneModeListener.BLUETOOTH_APM_STATE;
20 
21 import android.annotation.RequiresPermission;
22 import android.app.ActivityManager;
23 import android.bluetooth.BluetoothA2dp;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothHearingAid;
26 import android.bluetooth.BluetoothLeAudio;
27 import android.bluetooth.BluetoothProfile;
28 import android.bluetooth.BluetoothProfile.ServiceListener;
29 import android.content.Context;
30 import android.content.pm.PackageManager;
31 import android.content.res.Resources;
32 import android.os.Process;
33 import android.os.UserHandle;
34 import android.provider.Settings;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 /**
41  * Helper class that handles callout and callback methods without
42  * complex logic.
43  */
44 public class BluetoothModeChangeHelper {
45     private static final String TAG = "BluetoothModeChangeHelper";
46 
47     private volatile BluetoothA2dp mA2dp;
48     private volatile BluetoothHearingAid mHearingAid;
49     private volatile BluetoothLeAudio mLeAudio;
50     private final BluetoothAdapter mAdapter;
51     private final Context mContext;
52 
53     private String mBluetoothPackageName;
54 
BluetoothModeChangeHelper(Context context)55     BluetoothModeChangeHelper(Context context) {
56         mAdapter = BluetoothAdapter.getDefaultAdapter();
57         mContext = context;
58 
59         mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
60         mAdapter.getProfileProxy(mContext, mProfileServiceListener,
61                 BluetoothProfile.HEARING_AID);
62         mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO);
63     }
64 
65     private final ServiceListener mProfileServiceListener = new ServiceListener() {
66         @Override
67         public void onServiceConnected(int profile, BluetoothProfile proxy) {
68             // Setup Bluetooth profile proxies
69             switch (profile) {
70                 case BluetoothProfile.A2DP:
71                     mA2dp = (BluetoothA2dp) proxy;
72                     break;
73                 case BluetoothProfile.HEARING_AID:
74                     mHearingAid = (BluetoothHearingAid) proxy;
75                     break;
76                 case BluetoothProfile.LE_AUDIO:
77                     mLeAudio = (BluetoothLeAudio) proxy;
78                     break;
79                 default:
80                     break;
81             }
82         }
83 
84         @Override
85         public void onServiceDisconnected(int profile) {
86             // Clear Bluetooth profile proxies
87             switch (profile) {
88                 case BluetoothProfile.A2DP:
89                     mA2dp = null;
90                     break;
91                 case BluetoothProfile.HEARING_AID:
92                     mHearingAid = null;
93                     break;
94                 case BluetoothProfile.LE_AUDIO:
95                     mLeAudio = null;
96                     break;
97                 default:
98                     break;
99             }
100         }
101     };
102 
103     @VisibleForTesting
isMediaProfileConnected()104     public boolean isMediaProfileConnected() {
105         return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected();
106     }
107 
108     @VisibleForTesting
isBluetoothOn()109     public boolean isBluetoothOn() {
110         final BluetoothAdapter adapter = mAdapter;
111         if (adapter == null) {
112             return false;
113         }
114         return adapter.isLeEnabled();
115     }
116 
117     @VisibleForTesting
isAirplaneModeOn()118     public boolean isAirplaneModeOn() {
119         return Settings.Global.getInt(mContext.getContentResolver(),
120                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
121     }
122 
123     @VisibleForTesting
124     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
onAirplaneModeChanged(BluetoothManagerService managerService)125     public void onAirplaneModeChanged(BluetoothManagerService managerService) {
126         managerService.onAirplaneModeChanged();
127     }
128 
129     @VisibleForTesting
getSettingsInt(String name)130     public int getSettingsInt(String name) {
131         return Settings.Global.getInt(mContext.getContentResolver(),
132                 name, 0);
133     }
134 
135     @VisibleForTesting
setSettingsInt(String name, int value)136     public void setSettingsInt(String name, int value) {
137         Settings.Global.putInt(mContext.getContentResolver(),
138                 name, value);
139     }
140 
141     /**
142      * Helper method to get Settings Secure Int value
143      */
getSettingsSecureInt(String name, int def)144     public int getSettingsSecureInt(String name, int def) {
145         Context userContext = mContext.createContextAsUser(
146                 UserHandle.of(ActivityManager.getCurrentUser()), 0);
147         return Settings.Secure.getInt(userContext.getContentResolver(), name, def);
148     }
149 
150     /**
151      * Helper method to set Settings Secure Int value
152      */
setSettingsSecureInt(String name, int value)153     public void setSettingsSecureInt(String name, int value) {
154         Context userContext = mContext.createContextAsUser(
155                 UserHandle.of(ActivityManager.getCurrentUser()), 0);
156         Settings.Secure.putInt(userContext.getContentResolver(), name, value);
157     }
158 
159     @VisibleForTesting
showToastMessage()160     public void showToastMessage() {
161         Resources r = mContext.getResources();
162         final CharSequence text = r.getString(Resources.getSystem().getIdentifier(
163                 "bluetooth_airplane_mode_toast", "string", "android"));
164         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
165     }
166 
isA2dpConnected()167     private boolean isA2dpConnected() {
168         final BluetoothA2dp a2dp = mA2dp;
169         if (a2dp == null) {
170             return false;
171         }
172         return a2dp.getConnectedDevices().size() > 0;
173     }
174 
isHearingAidConnected()175     private boolean isHearingAidConnected() {
176         final BluetoothHearingAid hearingAid = mHearingAid;
177         if (hearingAid == null) {
178             return false;
179         }
180         return hearingAid.getConnectedDevices().size() > 0;
181     }
182 
isLeAudioConnected()183     private boolean isLeAudioConnected() {
184         final BluetoothLeAudio leAudio = mLeAudio;
185         if (leAudio == null) {
186             return false;
187         }
188         return leAudio.getConnectedDevices().size() > 0;
189     }
190 
191     /**
192      * Helper method to check whether BT should be enabled on APM
193      */
isBluetoothOnAPM()194     public boolean isBluetoothOnAPM() {
195         Context userContext = mContext.createContextAsUser(
196                 UserHandle.of(ActivityManager.getCurrentUser()), 0);
197         return Settings.Secure.getInt(userContext.getContentResolver(),
198                 BLUETOOTH_APM_STATE, 0) == 1;
199     }
200 
201     /**
202      * Helper method to retrieve BT package name with APM resources
203      */
getBluetoothPackageName()204     public String getBluetoothPackageName() {
205         if (mBluetoothPackageName != null) {
206             return mBluetoothPackageName;
207         }
208         var allPackages = mContext.getPackageManager().getPackagesForUid(Process.BLUETOOTH_UID);
209         for (String candidatePackage : allPackages) {
210             Resources resources;
211             try {
212                 resources = mContext.getPackageManager()
213                         .getResourcesForApplication(candidatePackage);
214             } catch (PackageManager.NameNotFoundException e) {
215                 // ignore, try next package
216                 Log.e(TAG, "Could not find package " + candidatePackage);
217                 continue;
218             } catch (Exception e) {
219                 Log.e(TAG, "Error while loading package" + e);
220                 continue;
221             }
222             if (resources.getIdentifier("bluetooth_and_wifi_stays_on_title",
223                     "string", candidatePackage) == 0) {
224                 continue;
225             }
226             mBluetoothPackageName = candidatePackage;
227         }
228         return mBluetoothPackageName;
229     }
230 }
231