summaryrefslogtreecommitdiffstats
path: root/java/com/android/dialer/telecom/TelecomUtil.java
blob: 82b43835fa95c64566255745c4f8e12bf16d2dfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dialer.telecom;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.CallLog.Calls;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.ContextCompat;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import com.android.dialer.common.LogUtil;
import java.util.ArrayList;
import java.util.List;

/**
 * Performs permission checks before calling into TelecomManager. Each method is self-explanatory -
 * perform the required check and return the fallback default if the permission is missing,
 * otherwise return the value from TelecomManager.
 */
public abstract class TelecomUtil {

  private static final String TAG = "TelecomUtil";
  private static boolean sWarningLogged = false;

  private static TelecomUtilImpl instance = new TelecomUtilImpl();

  @VisibleForTesting(otherwise = VisibleForTesting.NONE)
  public static void setInstanceForTesting(TelecomUtilImpl instanceForTesting) {
    instance = instanceForTesting;
  }

  public static void showInCallScreen(Context context, boolean showDialpad) {
    if (hasReadPhoneStatePermission(context)) {
      try {
        getTelecomManager(context).showInCallScreen(showDialpad);
      } catch (SecurityException e) {
        // Just in case
        LogUtil.w(TAG, "TelecomManager.showInCallScreen called without permission.");
      }
    }
  }

  public static void silenceRinger(Context context) {
    if (hasModifyPhoneStatePermission(context)) {
      try {
        getTelecomManager(context).silenceRinger();
      } catch (SecurityException e) {
        // Just in case
        LogUtil.w(TAG, "TelecomManager.silenceRinger called without permission.");
      }
    }
  }

  public static void cancelMissedCallsNotification(Context context) {
    if (hasModifyPhoneStatePermission(context)) {
      try {
        getTelecomManager(context).cancelMissedCallsNotification();
      } catch (SecurityException e) {
        LogUtil.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
      }
    }
  }

  public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
    if (hasModifyPhoneStatePermission(context)) {
      try {
        return getTelecomManager(context).getAdnUriForPhoneAccount(handle);
      } catch (SecurityException e) {
        LogUtil.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
      }
    }
    return null;
  }

  public static boolean handleMmi(
      Context context, String dialString, @Nullable PhoneAccountHandle handle) {
    if (hasModifyPhoneStatePermission(context)) {
      try {
        if (handle == null) {
          return getTelecomManager(context).handleMmi(dialString);
        } else {
          return getTelecomManager(context).handleMmi(dialString, handle);
        }
      } catch (SecurityException e) {
        LogUtil.w(TAG, "TelecomManager.handleMmi called without permission.");
      }
    }
    return false;
  }

  @Nullable
  public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(
      Context context, String uriScheme) {
    if (hasReadPhoneStatePermission(context)) {
      return getTelecomManager(context).getDefaultOutgoingPhoneAccount(uriScheme);
    }
    return null;
  }

  public static PhoneAccount getPhoneAccount(Context context, PhoneAccountHandle handle) {
    return getTelecomManager(context).getPhoneAccount(handle);
  }

  public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
    if (hasReadPhoneStatePermission(context)) {
      return getTelecomManager(context).getCallCapablePhoneAccounts();
    }
    return new ArrayList<>();
  }

  public static boolean isInCall(Context context) {
    return instance.isInCall(context);
  }

  public static boolean isVoicemailNumber(
      Context context, PhoneAccountHandle accountHandle, String number) {
    if (hasReadPhoneStatePermission(context)) {
      return getTelecomManager(context).isVoiceMailNumber(accountHandle, number);
    }
    return false;
  }

  @Nullable
  public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) {
    if (hasReadPhoneStatePermission(context)) {
      return getTelecomManager(context).getVoiceMailNumber(accountHandle);
    }
    return null;
  }

  /**
   * Tries to place a call using the {@link TelecomManager}.
   *
   * @param context context.
   * @param intent the call intent.
   * @return {@code true} if we successfully attempted to place the call, {@code false} if it failed
   *     due to a permission check.
   */
  public static boolean placeCall(Context context, Intent intent) {
    if (hasCallPhonePermission(context)) {
      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
      return true;
    }
    return false;
  }

  public static Uri getCallLogUri(Context context) {
    return hasReadWriteVoicemailPermissions(context)
        ? Calls.CONTENT_URI_WITH_VOICEMAIL
        : Calls.CONTENT_URI;
  }

  public static boolean hasReadWriteVoicemailPermissions(Context context) {
    return isDefaultDialer(context)
        || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
            && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
  }

  public static boolean hasModifyPhoneStatePermission(Context context) {
    return isDefaultDialer(context)
        || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
  }

  public static boolean hasReadPhoneStatePermission(Context context) {
    return isDefaultDialer(context) || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
  }

  public static boolean hasCallPhonePermission(Context context) {
    return isDefaultDialer(context) || hasPermission(context, Manifest.permission.CALL_PHONE);
  }

  private static boolean hasPermission(Context context, String permission) {
    return instance.hasPermission(context, permission);
  }

  private static TelecomManager getTelecomManager(Context context) {
    return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
  }

  public static boolean isDefaultDialer(Context context) {
    return instance.isDefaultDialer(context);
  }

  /** Contains an implementation for {@link TelecomUtil} methods */
  @VisibleForTesting()
  public static class TelecomUtilImpl {

    public boolean isInCall(Context context) {
      if (hasReadPhoneStatePermission(context)) {
        return getTelecomManager(context).isInCall();
      }
      return false;
    }

    public boolean hasPermission(Context context, String permission) {
      return ContextCompat.checkSelfPermission(context, permission)
          == PackageManager.PERMISSION_GRANTED;
    }

    public boolean isDefaultDialer(Context context) {
      final boolean result =
          TextUtils.equals(
              context.getPackageName(), getTelecomManager(context).getDefaultDialerPackage());
      if (result) {
        sWarningLogged = false;
      } else {
        if (!sWarningLogged) {
          // Log only once to prevent spam.
          LogUtil.w(TAG, "Dialer is not currently set to be default dialer");
          sWarningLogged = true;
        }
      }
      return result;
    }
  }
}