diff options
| author | 2019-02-05 10:33:08 -0800 | |
|---|---|---|
| committer | 2019-02-05 10:33:08 -0800 | |
| commit | 569a38a931279fba3bb342182e9d7ed48d24c1d4 (patch) | |
| tree | f3e5a36dcb91eec233b56d2a3d0c6243ae76fda2 | |
| parent | 1732e54ca5c6c00ab6d903f02b876d30564f61e7 (diff) | |
| parent | ef4110074efd2d11576ed6f8b125712f8974043e (diff) | |
Merge "Add a Telephony Debug Surface"
am: ef4110074e
Change-Id: I0a610d1e2e84411b7657cd0617075f59d06e760f
| -rw-r--r-- | api/system-current.txt | 3 | ||||
| -rw-r--r-- | telephony/java/android/telephony/DebugEventReporter.java | 143 | ||||
| -rw-r--r-- | telephony/java/android/telephony/TelephonyManager.java | 32 |
3 files changed, 178 insertions, 0 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 8046006872c1..8a7cf2e8bdde 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6365,6 +6365,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff(); method public void updateServiceLocation(); + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT"; field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED"; field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"; @@ -6372,6 +6373,8 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff + field public static final String EXTRA_DEBUG_EVENT_DESCRIPTION = "android.telephony.extra.DEBUG_EVENT_DESCRIPTION"; + field public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID"; field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; diff --git a/telephony/java/android/telephony/DebugEventReporter.java b/telephony/java/android/telephony/DebugEventReporter.java new file mode 100644 index 000000000000..586d3c57f38d --- /dev/null +++ b/telephony/java/android/telephony/DebugEventReporter.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 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 android.telephony; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.ParcelUuid; + +import java.util.List; +import java.util.UUID; + +/** + * A Simple Surface for Telephony to notify a loosely-coupled debugger of particular issues. + * + * DebugEventReporter allows an optional external logging component to receive events detected by + * the framework and take action. This log surface is designed to provide maximium flexibility + * to the receiver of these events. Envisioned use cases of this include notifying a vendor + * component of: an event that necessitates (timely) log collection on non-AOSP components; + * notifying a vendor component of a rare event that should prompt further action such as a + * bug report or user intervention for debug purposes. + * + * <p>This surface is not intended to enable a diagnostic monitor, nor is it intended to support + * streaming logs. + * + * @hide + */ +public final class DebugEventReporter { + private static final String TAG = "DebugEventReporter"; + + private static Context sContext = null; + + /* + * Because this is only supporting system packages, once we find a package, it will be the + * same package until the next system upgrade. Thus, to save time in processing debug events + * we can cache this info and skip the resolution process after it's done the first time. + */ + private static String sDebugPackageName = null; + + private DebugEventReporter() {}; + + /** + * If enabled, build and send an intent to a Debug Service for logging. + * + * This method sends the {@link TelephonyManager#DEBUG_EVENT DEBUG_EVENT} broadcast, which is + * system protected. Invoking this method unless you are the system will result in an error. + * + * @param eventId a fixed event ID that will be sent for each instance of the same event. This + * ID should be generated randomly. + * @param description an optional description, that if included will be used as the subject for + * identification and discussion of this event. This description should ideally be + * static and must not contain any sensitive information (especially PII). + */ + public static void sendEvent(@NonNull UUID eventId, String description) { + if (sContext == null) { + Rlog.w(TAG, "DebugEventReporter not yet initialized, dropping event=" + eventId); + return; + } + + // Even if we are initialized, that doesn't mean that a package name has been found. + // This is normal in many cases, such as when no debug package is installed on the system, + // so drop these events silently. + if (sDebugPackageName == null) return; + + Intent dbgIntent = new Intent(TelephonyManager.ACTION_DEBUG_EVENT); + dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_ID, new ParcelUuid(eventId)); + if (description != null) { + dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_DESCRIPTION, description); + } + dbgIntent.setPackage(sDebugPackageName); + sContext.sendBroadcast(dbgIntent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); + } + + /** + * Initialize the DebugEventReporter with the current context. + * + * This method must be invoked before any calls to sendEvent() will succeed. This method should + * only be invoked at most once. + * + * @param context a Context object used to initialize this singleton DebugEventReporter in + * the current process. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public static void initialize(@NonNull Context context) { + if (context == null) { + throw new IllegalArgumentException("DebugEventReporter needs a non-null context."); + } + + // Ensure that this context has sufficient permissions to send debug events. + context.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, + "This app does not have privileges to send debug events"); + + sContext = context; + + // Check to see if there is a valid debug package; if there are multiple, that's a config + // error, so just take the first one. + PackageManager pm = sContext.getPackageManager(); + if (pm == null) return; + List<ResolveInfo> packages = pm.queryBroadcastReceivers( + new Intent(TelephonyManager.ACTION_DEBUG_EVENT), + PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + if (packages == null || packages.isEmpty()) return; + if (packages.size() > 1) { + Rlog.e(TAG, "Multiple DebugEvent Receivers installed."); + } + + for (ResolveInfo r : packages) { + if (r.activityInfo == null + || pm.checkPermission( + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + r.activityInfo.packageName) + != PackageManager.PERMISSION_GRANTED) { + Rlog.w(TAG, + "Found package without proper permissions or no activity" + + r.activityInfo.packageName); + continue; + } + Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName); + sDebugPackageName = r.activityInfo.packageName; + break; + } + // Initialization may only be performed once. + } +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index df1e9422f050..148563ac8029 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1341,6 +1341,38 @@ public class TelephonyManager { @SystemApi public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000; + /** + * Intent sent when an error occurs that debug tools should log and possibly take further + * action such as capturing vendor-specific logs. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT"; + + /** + * An arbitrary ParcelUuid which should be consistent for each occurrence of the same event. + * + * This field must be included in all events. + * + * @hide + */ + @SystemApi + public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID"; + + /** + * A freeform string description of the event. + * + * This field is optional for all events and as a guideline should not exceed 80 characters + * and should be as short as possible to convey the essence of the event. + * + * @hide + */ + @SystemApi + public static final String EXTRA_DEBUG_EVENT_DESCRIPTION = + "android.telephony.extra.DEBUG_EVENT_DESCRIPTION"; + // // // Device Info |