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 default 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 String mDefaultDialerPackage;
35     private final Timeouts.Adapter mTimeoutsAdapter;
36     private UserHandle mLocationPermissionGrantedToUser;
37     private long mLastEmergencyCallTimestampMillis;
38 
39     @VisibleForTesting
EmergencyCallHelper( Context context, String defaultDialerPackage, Timeouts.Adapter timeoutsAdapter)40     public EmergencyCallHelper(
41             Context context,
42             String defaultDialerPackage,
43             Timeouts.Adapter timeoutsAdapter) {
44         mContext = context;
45         mDefaultDialerPackage = defaultDialerPackage;
46         mTimeoutsAdapter = timeoutsAdapter;
47     }
48 
maybeGrantTemporaryLocationPermission(Call call, UserHandle userHandle)49     void maybeGrantTemporaryLocationPermission(Call call, UserHandle userHandle) {
50         if (shouldGrantTemporaryLocationPermission(call)) {
51             grantLocationPermission(userHandle, call);
52         }
53         if (call != null && call.isEmergencyCall()) {
54             recordEmergencyCallTime();
55         }
56     }
57 
maybeRevokeTemporaryLocationPermission()58     void maybeRevokeTemporaryLocationPermission() {
59         if (wasGrantedTemporaryLocationPermission()) {
60             revokeLocationPermission();
61         }
62     }
63 
getLastEmergencyCallTimeMillis()64     long getLastEmergencyCallTimeMillis() {
65         return mLastEmergencyCallTimestampMillis;
66     }
67 
recordEmergencyCallTime()68     private void recordEmergencyCallTime() {
69         mLastEmergencyCallTimestampMillis = System.currentTimeMillis();
70     }
71 
isInEmergencyCallbackWindow()72     private boolean isInEmergencyCallbackWindow() {
73         return System.currentTimeMillis() - getLastEmergencyCallTimeMillis()
74                 < mTimeoutsAdapter.getEmergencyCallbackWindowMillis(mContext.getContentResolver());
75     }
76 
shouldGrantTemporaryLocationPermission(Call call)77     private boolean shouldGrantTemporaryLocationPermission(Call call) {
78         if (!mContext.getResources().getBoolean(R.bool.grant_location_permission_enabled)) {
79             Log.i(this, "ShouldGrantTemporaryLocationPermission, disabled by config");
80             return false;
81         }
82         if (call == null) {
83             Log.i(this, "ShouldGrantTemporaryLocationPermission, no call");
84             return false;
85         }
86         if (!call.isEmergencyCall() && !isInEmergencyCallbackWindow()) {
87             Log.i(this, "ShouldGrantTemporaryLocationPermission, not emergency");
88             return false;
89         }
90         if (hasLocationPermission()) {
91             Log.i(this, "ShouldGrantTemporaryLocationPermission, already has location permission");
92             return false;
93         }
94         Log.i(this, "ShouldGrantTemporaryLocationPermission, returning true");
95         return true;
96     }
97 
grantLocationPermission(UserHandle userHandle, Call call)98     private void grantLocationPermission(UserHandle userHandle, Call call) {
99         Log.i(this, "Granting temporary location permission to " + mDefaultDialerPackage
100               + ", user: " + userHandle);
101         try {
102             mContext.getPackageManager().grantRuntimePermission(mDefaultDialerPackage,
103                 Manifest.permission.ACCESS_FINE_LOCATION, userHandle);
104             recordPermissionGrant(userHandle);
105         } catch (Exception e) {
106             Log.e(this, e, "Failed to grant location permission to " + mDefaultDialerPackage
107                   + ", user: " + userHandle);
108         }
109     }
110 
revokeLocationPermission()111     private void revokeLocationPermission() {
112         Log.i(this, "Revoking temporary location permission from " + mDefaultDialerPackage
113               + ", user: " + mLocationPermissionGrantedToUser);
114         UserHandle userHandle = mLocationPermissionGrantedToUser;
115         clearPermissionGrant();
116         try {
117             mContext.getPackageManager().revokeRuntimePermission(mDefaultDialerPackage,
118                   Manifest.permission.ACCESS_FINE_LOCATION, userHandle);
119         } catch (Exception e) {
120             Log.e(this, e, "Failed to revoke location permission from " + mDefaultDialerPackage
121                   + ", user: " + userHandle);
122         }
123     }
124 
hasLocationPermission()125     private boolean hasLocationPermission() {
126         return mContext.getPackageManager().checkPermission(
127                 Manifest.permission.ACCESS_FINE_LOCATION, mDefaultDialerPackage)
128                 == PackageManager.PERMISSION_GRANTED;
129     }
130 
recordPermissionGrant(UserHandle userHandle)131     private void recordPermissionGrant(UserHandle userHandle) {
132         mLocationPermissionGrantedToUser = userHandle;
133     }
134 
wasGrantedTemporaryLocationPermission()135     private boolean wasGrantedTemporaryLocationPermission() {
136         return mLocationPermissionGrantedToUser != null;
137     }
138 
clearPermissionGrant()139     private void clearPermissionGrant() {
140         mLocationPermissionGrantedToUser = null;
141     }
142 }
143