diff options
| -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  |