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