1 /* 2 * Copyright 2016, 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.telecom; 18 19 import android.Manifest; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.os.UserHandle; 23 import android.telecom.Log; 24 import com.android.internal.annotations.VisibleForTesting; 25 26 /** 27 * Helps with emergency calls by: 28 * 1. granting temporary location permission to the system dialer service during emergency calls 29 * 2. keeping track of the time of the last emergency call 30 */ 31 @VisibleForTesting 32 public class EmergencyCallHelper { 33 private final Context mContext; 34 private final DefaultDialerCache mDefaultDialerCache; 35 private final Timeouts.Adapter mTimeoutsAdapter; 36 private UserHandle mLocationPermissionGrantedToUser; 37 private boolean mHadFineLocation = false; 38 private boolean mHadBackgroundLocation = false; 39 private long mLastEmergencyCallTimestampMillis; 40 41 @VisibleForTesting EmergencyCallHelper( Context context, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter)42 public EmergencyCallHelper( 43 Context context, 44 DefaultDialerCache defaultDialerCache, 45 Timeouts.Adapter timeoutsAdapter) { 46 mContext = context; 47 mDefaultDialerCache = defaultDialerCache; 48 mTimeoutsAdapter = timeoutsAdapter; 49 } 50 maybeGrantTemporaryLocationPermission(Call call, UserHandle userHandle)51 void maybeGrantTemporaryLocationPermission(Call call, UserHandle userHandle) { 52 if (shouldGrantTemporaryLocationPermission(call)) { 53 grantLocationPermission(userHandle); 54 } 55 if (call != null && call.isEmergencyCall()) { 56 recordEmergencyCallTime(); 57 } 58 } 59 maybeRevokeTemporaryLocationPermission()60 void maybeRevokeTemporaryLocationPermission() { 61 if (wasGrantedTemporaryLocationPermission()) { 62 revokeLocationPermission(); 63 } 64 } 65 getLastEmergencyCallTimeMillis()66 long getLastEmergencyCallTimeMillis() { 67 return mLastEmergencyCallTimestampMillis; 68 } 69 recordEmergencyCallTime()70 private void recordEmergencyCallTime() { 71 mLastEmergencyCallTimestampMillis = System.currentTimeMillis(); 72 } 73 isInEmergencyCallbackWindow()74 private boolean isInEmergencyCallbackWindow() { 75 return System.currentTimeMillis() - getLastEmergencyCallTimeMillis() 76 < mTimeoutsAdapter.getEmergencyCallbackWindowMillis(mContext.getContentResolver()); 77 } 78 shouldGrantTemporaryLocationPermission(Call call)79 private boolean shouldGrantTemporaryLocationPermission(Call call) { 80 if (!mContext.getResources().getBoolean(R.bool.grant_location_permission_enabled)) { 81 Log.i(this, "ShouldGrantTemporaryLocationPermission, disabled by config"); 82 return false; 83 } 84 if (call == null) { 85 Log.i(this, "ShouldGrantTemporaryLocationPermission, no call"); 86 return false; 87 } 88 if (!call.isEmergencyCall() && !isInEmergencyCallbackWindow()) { 89 Log.i(this, "ShouldGrantTemporaryLocationPermission, not emergency"); 90 return false; 91 } 92 Log.i(this, "ShouldGrantTemporaryLocationPermission, returning true"); 93 return true; 94 } 95 grantLocationPermission(UserHandle userHandle)96 private void grantLocationPermission(UserHandle userHandle) { 97 String systemDialerPackage = mDefaultDialerCache.getSystemDialerApplication(); 98 Log.i(this, "Granting temporary location permission to " + systemDialerPackage 99 + ", user: " + userHandle); 100 try { 101 boolean hadBackgroundLocation = hasBackgroundLocationPermission(); 102 boolean hadFineLocation = hasFineLocationPermission(); 103 if (hadBackgroundLocation && hadFineLocation) { 104 Log.i(this, "Skipping location grant because the system dialer already" 105 + " holds sufficient permissions"); 106 return; 107 } 108 if (!hadFineLocation) { 109 mContext.getPackageManager().grantRuntimePermission(systemDialerPackage, 110 Manifest.permission.ACCESS_FINE_LOCATION, userHandle); 111 } 112 if (!hadBackgroundLocation) { 113 mContext.getPackageManager().grantRuntimePermission(systemDialerPackage, 114 Manifest.permission.ACCESS_BACKGROUND_LOCATION, userHandle); 115 } 116 mHadFineLocation = hadFineLocation; 117 mHadBackgroundLocation = hadBackgroundLocation; 118 recordPermissionGrant(userHandle); 119 } catch (Exception e) { 120 Log.e(this, e, "Failed to grant location permissions to " + systemDialerPackage 121 + ", user: " + userHandle); 122 } 123 } 124 revokeLocationPermission()125 private void revokeLocationPermission() { 126 String systemDialerPackage = mDefaultDialerCache.getSystemDialerApplication(); 127 Log.i(this, "Revoking temporary location permission from " + systemDialerPackage 128 + ", user: " + mLocationPermissionGrantedToUser); 129 UserHandle userHandle = mLocationPermissionGrantedToUser; 130 try { 131 if (!mHadFineLocation) { 132 mContext.getPackageManager().revokeRuntimePermission(systemDialerPackage, 133 Manifest.permission.ACCESS_FINE_LOCATION, userHandle); 134 } 135 if (!mHadBackgroundLocation) { 136 mContext.getPackageManager().revokeRuntimePermission(systemDialerPackage, 137 Manifest.permission.ACCESS_BACKGROUND_LOCATION, userHandle); 138 } 139 } catch (Exception e) { 140 Log.e(this, e, "Failed to revoke location permission from " + systemDialerPackage 141 + ", user: " + userHandle); 142 } 143 clearPermissionGrant(); 144 } 145 hasBackgroundLocationPermission()146 private boolean hasBackgroundLocationPermission() { 147 return mContext.getPackageManager().checkPermission( 148 Manifest.permission.ACCESS_BACKGROUND_LOCATION, 149 mDefaultDialerCache.getSystemDialerApplication()) 150 == PackageManager.PERMISSION_GRANTED; 151 } 152 hasFineLocationPermission()153 private boolean hasFineLocationPermission() { 154 return mContext.getPackageManager().checkPermission( 155 Manifest.permission.ACCESS_FINE_LOCATION, 156 mDefaultDialerCache.getSystemDialerApplication()) 157 == PackageManager.PERMISSION_GRANTED; 158 } 159 recordPermissionGrant(UserHandle userHandle)160 private void recordPermissionGrant(UserHandle userHandle) { 161 mLocationPermissionGrantedToUser = userHandle; 162 } 163 wasGrantedTemporaryLocationPermission()164 private boolean wasGrantedTemporaryLocationPermission() { 165 return mLocationPermissionGrantedToUser != null; 166 } 167 clearPermissionGrant()168 private void clearPermissionGrant() { 169 mLocationPermissionGrantedToUser = null; 170 mHadBackgroundLocation = false; 171 mHadFineLocation = false; 172 } 173 } 174