diff options
| author | 2016-01-18 10:00:55 +0000 | |
|---|---|---|
| committer | 2016-01-18 10:00:55 +0000 | |
| commit | a8c8851caa0d1d44eccfcae2e01a2b6e7235d290 (patch) | |
| tree | 84431c530fd476665a1496e937d5b68134210c8c | |
| parent | ac3e599069e1b87ea190f008aef60a506c8561c7 (diff) | |
| parent | 3fc437e89b018f258a3dee1a83014555aa156dc4 (diff) | |
Merge "DPM changes to support remote bugreports"
| -rw-r--r-- | api/current.txt | 9 | ||||
| -rw-r--r-- | api/system-current.txt | 9 | ||||
| -rw-r--r-- | api/test-current.txt | 9 | ||||
| -rw-r--r-- | core/java/android/app/admin/DeviceAdminReceiver.java | 124 | ||||
| -rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 22 | ||||
| -rw-r--r-- | core/java/android/app/admin/IDevicePolicyManager.aidl | 2 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 6 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 19 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 9 | ||||
| -rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 263 | ||||
| -rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/Owners.java | 62 | ||||
| -rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java | 116 |
12 files changed, 641 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt index 90228a9a9517..0f3b09963d64 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5737,6 +5737,9 @@ package android.app.admin { ctor public DeviceAdminReceiver(); method public android.app.admin.DevicePolicyManager getManager(android.content.Context); method public android.content.ComponentName getWho(android.content.Context); + method public void onBugreportFailed(android.content.Context, android.content.Intent, int); + method public void onBugreportShared(android.content.Context, android.content.Intent, java.lang.String); + method public void onBugreportSharingDeclined(android.content.Context, android.content.Intent); method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String); method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent); method public void onDisabled(android.content.Context, android.content.Intent); @@ -5761,11 +5764,16 @@ package android.app.admin { field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"; field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED"; field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; + field public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0; // 0x0 + field public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1; // 0x1 field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin"; field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation { + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -5844,6 +5852,7 @@ package android.app.admin { method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); method public boolean removeKeyPair(android.content.ComponentName, java.lang.String); method public boolean removeUser(android.content.ComponentName, android.os.UserHandle); + method public boolean requestBugreport(android.content.ComponentName); method public boolean resetPassword(java.lang.String, int); method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index a194e6e23f0b..360a40bddfc7 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5870,6 +5870,9 @@ package android.app.admin { ctor public DeviceAdminReceiver(); method public android.app.admin.DevicePolicyManager getManager(android.content.Context); method public android.content.ComponentName getWho(android.content.Context); + method public void onBugreportFailed(android.content.Context, android.content.Intent, int); + method public void onBugreportShared(android.content.Context, android.content.Intent, java.lang.String); + method public void onBugreportSharingDeclined(android.content.Context, android.content.Intent); method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String); method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent); method public void onDisabled(android.content.Context, android.content.Intent); @@ -5894,11 +5897,16 @@ package android.app.admin { field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"; field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED"; field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; + field public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0; // 0x0 + field public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1; // 0x1 field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin"; field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation { + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -5986,6 +5994,7 @@ package android.app.admin { method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); method public boolean removeKeyPair(android.content.ComponentName, java.lang.String); method public boolean removeUser(android.content.ComponentName, android.os.UserHandle); + method public boolean requestBugreport(android.content.ComponentName); method public boolean resetPassword(java.lang.String, int); method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException; diff --git a/api/test-current.txt b/api/test-current.txt index 3c271f319e6f..fb11fcc26b2e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5739,6 +5739,9 @@ package android.app.admin { ctor public DeviceAdminReceiver(); method public android.app.admin.DevicePolicyManager getManager(android.content.Context); method public android.content.ComponentName getWho(android.content.Context); + method public void onBugreportFailed(android.content.Context, android.content.Intent, int); + method public void onBugreportShared(android.content.Context, android.content.Intent, java.lang.String); + method public void onBugreportSharingDeclined(android.content.Context, android.content.Intent); method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String); method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent); method public void onDisabled(android.content.Context, android.content.Intent); @@ -5763,11 +5766,16 @@ package android.app.admin { field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"; field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED"; field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; + field public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0; // 0x0 + field public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1; // 0x1 field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin"; field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public static abstract class DeviceAdminReceiver.BugreportFailureCode implements java.lang.annotation.Annotation { + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -5846,6 +5854,7 @@ package android.app.admin { method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); method public boolean removeKeyPair(android.content.ComponentName, java.lang.String); method public boolean removeUser(android.content.ComponentName, android.os.UserHandle); + method public boolean requestBugreport(android.content.ComponentName); method public boolean resetPassword(java.lang.String, int); method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String); diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 84b6d392b83e..1c4dcf79f233 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -17,6 +17,7 @@ package android.app.admin; import android.accounts.AccountManager; +import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -29,6 +30,9 @@ import android.net.Uri; import android.os.Bundle; import android.security.KeyChain; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Base class for implementing a device administration component. This * class provides a convenience for interpreting the raw intent actions @@ -227,6 +231,75 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; + /** + * Action sent to a device administrator to notify that the device user + * has declined sharing a bugreport. + * + * <p>The calling device admin must be the device owner to receive this broadcast. + * @see DevicePolicyManager#requestBugreport + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BUGREPORT_SHARING_DECLINED = + "android.app.action.BUGREPORT_SHARING_DECLINED"; + + /** + * Action sent to a device administrator to notify that the collection of a bugreport + * has failed. + * + * <p>The calling device admin must be the device owner to receive this broadcast. + * @see DevicePolicyManager#requestBugreport + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BUGREPORT_FAILED = "android.app.action.BUGREPORT_FAILED"; + + /** + * Action sent to a device administrator to share the bugreport. + * + * <p>The calling device admin must be the device owner to receive this broadcast. + * @see DevicePolicyManager#requestBugreport + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BUGREPORT_SHARE = + "android.app.action.BUGREPORT_SHARE"; + + /** + * A string containing the SHA-256 hash of the bugreport file. + * + * @see #ACTION_BUGREPORT_SHARE + * @hide + */ + public static final String EXTRA_BUGREPORT_HASH = "android.app.extra.BUGREPORT_HASH"; + + /** + * An {@code int} failure code representing the reason of the bugreport failure. + * + * @see #ACTION_BUGREPORT_FAILED + * @see #BUGREPORT_FAILURE_FAILED_COMPLETING, #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE + * @hide + */ + public static final String EXTRA_BUGREPORT_FAILURE_REASON = + "android.app.extra.BUGREPORT_FAILURE_REASON"; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + BUGREPORT_FAILURE_FAILED_COMPLETING, + BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE + }) + /** + * An interface representing reason of bugreport failure. + * + * @see #EXTRA_BUGREPORT_FAILURE_REASON + * @hide + */ + public @interface BugreportFailureCode {} + /** Bugreport completion process failed. */ + public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0; + /** Bugreport is no longer available for collection. */ + public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1; + /** @hide */ public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS"; @@ -482,6 +555,48 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** + * Called when sharing a bugreport has been cancelled by the user of the device. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @see DevicePolicyManager#requestBugreport + */ + public void onBugreportSharingDeclined(Context context, Intent intent) { + } + + /** + * Called when the bugreport has been shared with the device administrator app. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. Contains the URI of + * the bugreport file (with MIME type "application/vnd.android.bugreport"), that can be accessed + * by calling {@link Intent#getData()} + * @param bugreportHash SHA-256 hash of the bugreport file. + * @see DevicePolicyManager#requestBugreport + */ + public void onBugreportShared(Context context, Intent intent, String bugreportHash) { + } + + /** + * Called when the bugreport collection flow has failed. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param failureCode int containing failure code. One of + * #BUGREPORT_FAILURE_FAILED_COMPLETING or #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE + * @see DevicePolicyManager#requestBugreport + */ + public void onBugreportFailed(Context context, Intent intent, + @BugreportFailureCode int failureCode) { + } + + /** * Intercept standard device administrator broadcasts. Implementations * should not override this method; it is better to implement the * convenience callbacks for each action. @@ -524,6 +639,15 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) { long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1); onSystemUpdatePending(context, intent, receivedTime); + } else if (ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) { + onBugreportSharingDeclined(context, intent); + } else if (ACTION_BUGREPORT_SHARE.equals(action)) { + String bugreportFileHash = intent.getStringExtra(EXTRA_BUGREPORT_HASH); + onBugreportShared(context, intent, bugreportFileHash); + } else if (ACTION_BUGREPORT_FAILED.equals(action)) { + int failureCode = intent.getIntExtra(EXTRA_BUGREPORT_FAILURE_REASON, + BUGREPORT_FAILURE_FAILED_COMPLETING); + onBugreportFailed(context, intent, failureCode); } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 45b23d0d1609..9e39a5fd7bbd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2560,6 +2560,28 @@ public class DevicePolicyManager { } /** + * Called by a device owner to request a bugreport. + * + * <p>There must be only one user on the device, managed by the device owner. + * Otherwise a security exception will be thrown. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return {@code true} if the bugreport collection started successfully, or {@code false} + * if it wasn't triggered because a previous bugreport operation is still active + * (either the bugreport is still running or waiting for the user to share or decline) + */ + public boolean requestBugreport(@NonNull ComponentName admin) { + if (mService != null) { + try { + return mService.requestBugreport(admin); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + /** * Determine whether or not creating a guest user has been disabled for the device * * @hide diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7771440dd9f2..08cab8846290 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -91,6 +91,8 @@ interface IDevicePolicyManager { boolean getStorageEncryption(in ComponentName who, int userHandle); int getStorageEncryptionStatus(int userHandle); + boolean requestBugreport(in ComponentName who); + void setCameraDisabled(in ComponentName who, boolean disabled); boolean getCameraDisabled(in ComponentName who, int userHandle); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b3cc2349bff7..a277568d7d99 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -94,6 +94,10 @@ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" /> + <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS" /> <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" /> <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" /> @@ -251,6 +255,8 @@ <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" /> <protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" /> <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" /> + <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" /> + <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" /> <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" /> <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" /> <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4ef3f598ac76..4ad36f5dea02 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2899,6 +2899,25 @@ <!-- Message of notification shown when ADB is actively connected to the phone. --> <string name="adb_active_notification_message">Touch to disable USB debugging.</string> + <!-- Title of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. STOPSHIP: this is not final --> + <string name="share_remote_bugreport_notification_title">Remote bugreport request</string> + <!-- Ticker of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. STOPSHIP: this is not final --> + <string name="share_remote_bugreport_notification_ticker">Remote bugreport request</string> + <!-- Message of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. STOPSHIP: this is not final --> + <string name="share_remote_bugreport_notification_message">Your IT admin requested remote bugreport to carry out incident investigation</string> + <!-- Title of notification shown for remote bugreport progress. STOPSHIP: this is not final --> + <string name="remote_bugreport_progress_notification_title">Remote bugreport</string> + <!-- Ticker of notification shown for remote bugreport progress. STOPSHIP: this is not final --> + <string name="remote_bugreport_progress_notification_ticker">Remote bugreport collection in progress</string> + <!-- Message of notification shown for remote bugreport progress. User can cancel the bugreport STOPSHIP: this is not final --> + <string name="remote_bugreport_progress_notification_message_can_cancel">Remote bugreport in progress. Touch to cancel.</string> + <!-- Message of notification shown for remote bugreport progress. User cannot cancel the bugreport STOPSHIP: this is not final --> + <string name="remote_bugreport_progress_notification_message_cannot_cancel">Remote bugreport in progress.</string> + <!-- Acceptance label of notification shown to ask for user consent for sharing the remote bugreport. STOPSHIP: this is not final --> + <string name="share_remote_bugreport_notification_accept">ACCEPT</string> + <!-- Decline label of notification shown to ask for user consent for sharing the remote bugreport. STOPSHIP: this is not final --> + <string name="share_remote_bugreport_notification_decline">DECLINE</string> + <!-- Used to replace %s in urls retreived from the signin server with locales. For Some --> <!-- devices we don't support all the locales we ship to and need to replace the '%s' with a --> <!-- locale string based on mcc values. By default (0-length string) we don't replace the %s --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index dc288d5d0223..b4cb66415c3d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1770,6 +1770,15 @@ <java-symbol type="string" name="accessibility_binding_label" /> <java-symbol type="string" name="adb_active_notification_message" /> <java-symbol type="string" name="adb_active_notification_title" /> + <java-symbol type="string" name="share_remote_bugreport_notification_title" /> + <java-symbol type="string" name="share_remote_bugreport_notification_ticker" /> + <java-symbol type="string" name="share_remote_bugreport_notification_message" /> + <java-symbol type="string" name="remote_bugreport_progress_notification_title" /> + <java-symbol type="string" name="remote_bugreport_progress_notification_ticker" /> + <java-symbol type="string" name="remote_bugreport_progress_notification_message_can_cancel" /> + <java-symbol type="string" name="remote_bugreport_progress_notification_message_cannot_cancel" /> + <java-symbol type="string" name="share_remote_bugreport_notification_accept" /> + <java-symbol type="string" name="share_remote_bugreport_notification_decline" /> <java-symbol type="string" name="aerr_application" /> <java-symbol type="string" name="aerr_process" /> <java-symbol type="string" name="aerr_process_silence" /> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f14b03207d95..1ada0ace4429 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -33,6 +33,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accounts.AccountManager; import android.annotation.NonNull; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.AppGlobals; @@ -82,6 +83,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; @@ -156,6 +158,7 @@ import java.util.Date; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; /** * Implementation of the device policy APIs. @@ -270,6 +273,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ private boolean mHasFeature; + private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean(); + private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean(); + + private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() { + @Override + public void run() { + if(mRemoteBugreportServiceIsActive.get()) { + onBugreportFailed(); + } + } + }; + + private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction()) + && mRemoteBugreportServiceIsActive.get()) { + onBugreportFinished(intent); + } + } + }; + + private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + mInjector.getNotificationManager().cancel(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID); + if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED.equals(action)) { + onBugreportSharingAccepted(); + } else if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED + .equals(action)) { + onBugreportSharingDeclined(); + } + mContext.unregisterReceiver(mRemoteBugreportConsentReceiver); + } + }; + public static final class Lifecycle extends SystemService { private DevicePolicyManagerService mService; @@ -343,6 +386,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final String action = intent.getAction(); final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); + + if (Intent.ACTION_BOOT_COMPLETED.equals(action) + && userHandle == mOwners.getDeviceOwnerUserId() + && getDeviceOwnerRemoteBugreportUri() != null) { + IntentFilter filterConsent = new IntentFilter(); + filterConsent.addAction( + RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED); + filterConsent.addAction( + RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED); + mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent); + mInjector.getNotificationManager().notify( + LOG_TAG, RemoteBugreportUtils.REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID, + RemoteBugreportUtils.buildRemoteBugreportConsentNotification(mContext)); + } if (Intent.ACTION_BOOT_COMPLETED.equals(action) || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) { if (VERBOSE_LOG) { @@ -4594,6 +4651,212 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private void ensureDeviceOwnerManagingSingleUser(ComponentName who) throws SecurityException { + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + } + final long callingIdentity = mInjector.binderClearCallingIdentity(); + try { + if (mInjector.userManagerIsSplitSystemUser()) { + // In split system user mode, only allow the case where the device owner is managing + // the only non-system user of the device + if (mUserManager.getUserCount() > 2 + || mOwners.getDeviceOwnerUserId() == UserHandle.USER_SYSTEM) { + throw new SecurityException( + "There should only be one user, managed by Device Owner"); + } + } else if (mUserManager.getUserCount() > 1) { + throw new SecurityException( + "There should only be one user, managed by Device Owner"); + } + } finally { + mInjector.binderRestoreCallingIdentity(callingIdentity); + } + } + + @Override + public boolean requestBugreport(ComponentName who) { + if (!mHasFeature) { + return false; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + ensureDeviceOwnerManagingSingleUser(who); + + if (mRemoteBugreportServiceIsActive.get() + || (getDeviceOwnerRemoteBugreportUri() != null)) { + Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running."); + return false; + } + + final long callingIdentity = mInjector.binderClearCallingIdentity(); + try { + ActivityManagerNative.getDefault().requestBugReport( + ActivityManager.BUGREPORT_OPTION_REMOTE); + + mRemoteBugreportServiceIsActive.set(true); + mRemoteBugreportSharingAccepted.set(false); + registerRemoteBugreportReceivers(); + mInjector.getNotificationManager().notify( + LOG_TAG, RemoteBugreportUtils.REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID, + RemoteBugreportUtils.buildRemoteBugreportConsentNotification(mContext)); + mInjector.getNotificationManager().notify( + LOG_TAG, RemoteBugreportUtils.REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID, + RemoteBugreportUtils.buildRemoteBugreportInProgressNotification(mContext, + /* canCancelBugReport */ true)); + mHandler.postDelayed(mRemoteBugreportTimeoutRunnable, + RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS); + return true; + } catch (RemoteException re) { + // should never happen + Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re); + return false; + } finally { + mInjector.binderRestoreCallingIdentity(callingIdentity); + } + } + + private synchronized void sendDeviceOwnerCommand(String action, Bundle extras) { + Intent intent = new Intent(action); + intent.setComponent(mOwners.getDeviceOwnerComponent()); + if (extras != null) { + intent.putExtras(extras); + } + mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId())); + } + + private synchronized String getDeviceOwnerRemoteBugreportUri() { + return mOwners.getDeviceOwnerRemoteBugreportUri(); + } + + private synchronized void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri, + String bugreportHash) { + mOwners.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUri, bugreportHash); + } + + private void registerRemoteBugreportReceivers() { + try { + IntentFilter filterFinished = new IntentFilter( + RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH, + RemoteBugreportUtils.BUGREPORT_MIMETYPE); + mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished); + } catch (IntentFilter.MalformedMimeTypeException e) { + // should never happen, as setting a constant + Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e); + } + IntentFilter filterConsent = new IntentFilter(); + filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED); + filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED); + mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent); + } + + private void onBugreportFinished(Intent intent) { + mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable); + mRemoteBugreportServiceIsActive.set(false); + mInjector.getNotificationManager().cancel(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID); + Uri bugreportUri = intent.getData(); + String bugreportUriString = null; + if (bugreportUri != null) { + bugreportUriString = bugreportUri.toString(); + } + String bugreportHash = intent.getStringExtra( + RemoteBugreportUtils.EXTRA_REMOTE_BUGREPORT_HASH); + if (mRemoteBugreportSharingAccepted.get()) { + shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash); + } else { + setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash); + } + mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); + } + + private void onBugreportFailed() { + mRemoteBugreportServiceIsActive.set(false); + mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP, + RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE); + mRemoteBugreportSharingAccepted.set(false); + setDeviceOwnerRemoteBugreportUriAndHash(null, null); + mInjector.getNotificationManager().cancel(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID); + mInjector.getNotificationManager().cancel(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID); + Bundle extras = new Bundle(); + extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON, + DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING); + sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras); + mContext.unregisterReceiver(mRemoteBugreportConsentReceiver); + mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); + } + + private void onBugreportSharingAccepted() { + mRemoteBugreportSharingAccepted.set(true); + String bugreportUriString = null; + String bugreportHash = null; + synchronized (this) { + bugreportUriString = getDeviceOwnerRemoteBugreportUri(); + bugreportHash = mOwners.getDeviceOwnerRemoteBugreportHash(); + } + if (bugreportUriString != null) { + shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash); + } else if (mRemoteBugreportServiceIsActive.get()) { + mInjector.getNotificationManager().notify(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID, + RemoteBugreportUtils.buildRemoteBugreportInProgressNotification(mContext, + /* canCancelBugReport */ false)); + } + } + + private void onBugreportSharingDeclined() { + if (mRemoteBugreportServiceIsActive.get()) { + mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP, + RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE); + mRemoteBugreportServiceIsActive.set(false); + mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable); + mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); + } + mRemoteBugreportSharingAccepted.set(false); + setDeviceOwnerRemoteBugreportUriAndHash(null, null); + mInjector.getNotificationManager().cancel(LOG_TAG, + RemoteBugreportUtils.REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID); + sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null); + } + + private void shareBugreportWithDeviceOwnerIfExists(String bugreportUriString, + String bugreportHash) { + ParcelFileDescriptor pfd = null; + try { + if (bugreportUriString == null) { + throw new FileNotFoundException(); + } + Uri bugreportUri = Uri.parse(bugreportUriString); + pfd = mContext.getContentResolver().openFileDescriptor(bugreportUri, "r"); + + synchronized (this) { + Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE); + intent.setComponent(mOwners.getDeviceOwnerComponent()); + intent.setDataAndType(bugreportUri, RemoteBugreportUtils.BUGREPORT_MIMETYPE); + intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash); + mContext.grantUriPermission(mOwners.getDeviceOwnerComponent().getPackageName(), + bugreportUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId())); + } + } catch (FileNotFoundException e) { + Bundle extras = new Bundle(); + extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON, + DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE); + sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras); + } finally { + try { + if (pfd != null) { + pfd.close(); + } + } catch (IOException ex) { + // Ignore + } + mRemoteBugreportSharingAccepted.set(false); + setDeviceOwnerRemoteBugreportUriAndHash(null, null); + } + } + /** * Disables all device cameras according to the specified admin. */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index f7de0b3896f7..880f810107a0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -77,6 +77,8 @@ class Owners { private static final String ATTR_NAME = "name"; private static final String ATTR_PACKAGE = "package"; private static final String ATTR_COMPONENT_NAME = "component"; + private static final String ATTR_REMOTE_BUGREPORT_URI = "remoteBugreportUri"; + private static final String ATTR_REMOTE_BUGREPORT_HASH = "remoteBugreportHash"; private static final String ATTR_USERID = "userId"; private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated"; @@ -161,6 +163,14 @@ class Owners { return mDeviceOwner != null ? mDeviceOwner.admin : null; } + String getDeviceOwnerRemoteBugreportUri() { + return mDeviceOwner != null ? mDeviceOwner.remoteBugreportUri : null; + } + + String getDeviceOwnerRemoteBugreportHash() { + return mDeviceOwner != null ? mDeviceOwner.remoteBugreportHash : null; + } + void setDeviceOwner(ComponentName admin, String ownerName, int userId) { if (userId < 0) { Slog.e(TAG, "Invalid user id for device owner user: " + userId); @@ -175,7 +185,8 @@ class Owners { // userRestrictionsMigrated should always be true. void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId, boolean userRestrictionsMigrated) { - mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated); + mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated, + /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null); mDeviceOwnerUserId = userId; mUserManagerInternal.setDeviceManaged(true); @@ -191,7 +202,8 @@ class Owners { void setProfileOwner(ComponentName admin, String ownerName, int userId) { // For a newly set PO, there's no need for migration. mProfileOwners.put(userId, new OwnerInfo(ownerName, admin, - /* userRestrictionsMigrated =*/ true)); + /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null, + /* remoteBugreportHash =*/ null)); mUserManagerInternal.setUserManaged(userId, true); } @@ -266,6 +278,16 @@ class Owners { writeDeviceOwner(); } + /** Sets the remote bugreport uri and hash, and also writes to the file. */ + void setDeviceOwnerRemoteBugreportUriAndHash(String remoteBugreportUri, + String remoteBugreportHash) { + if (mDeviceOwner != null) { + mDeviceOwner.remoteBugreportUri = remoteBugreportUri; + mDeviceOwner.remoteBugreportHash = remoteBugreportHash; + } + writeDeviceOwner(); + } + /** Sets the user restrictions migrated flag, and also writes to the file. */ void setProfileOwnerUserRestrictionsMigrated(int userId) { OwnerInfo profileOwner = mProfileOwners.get(userId); @@ -295,7 +317,8 @@ class Owners { String name = parser.getAttributeValue(null, ATTR_NAME); String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); mDeviceOwner = new OwnerInfo(name, packageName, - /* userRestrictionsMigrated =*/ false); + /* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null, + /* remoteBugreportHash =*/ null); mDeviceOwnerUserId = UserHandle.USER_SYSTEM; } else if (tag.equals(TAG_DEVICE_INITIALIZER)) { // Deprecated tag @@ -311,7 +334,7 @@ class Owners { profileOwnerComponentStr); if (admin != null) { profileOwnerInfo = new OwnerInfo(profileOwnerName, admin, - /* userRestrictionsMigrated =*/ false); + /* userRestrictionsMigrated =*/ false, null, null); } else { // This shouldn't happen but switch from package name -> component name // might have written bad device owner files. b/17652534 @@ -321,7 +344,8 @@ class Owners { } if (profileOwnerInfo == null) { profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName, - /* userRestrictionsMigrated =*/ false); + /* userRestrictionsMigrated =*/ false, + /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null); } mProfileOwners.put(userId, profileOwnerInfo); } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) { @@ -579,19 +603,27 @@ class Owners { public final String packageName; public final ComponentName admin; public boolean userRestrictionsMigrated; + public String remoteBugreportUri; + public String remoteBugreportHash; - public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated) { + public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated, + String remoteBugreportUri, String remoteBugreportHash) { this.name = name; this.packageName = packageName; this.admin = new ComponentName(packageName, ""); this.userRestrictionsMigrated = userRestrictionsMigrated; + this.remoteBugreportUri = remoteBugreportUri; + this.remoteBugreportHash = remoteBugreportHash; } - public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated) { + public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated, + String remoteBugreportUri, String remoteBugreportHash) { this.name = name; this.admin = admin; this.packageName = admin.getPackageName(); this.userRestrictionsMigrated = userRestrictionsMigrated; + this.remoteBugreportUri = remoteBugreportUri; + this.remoteBugreportHash = remoteBugreportHash; } public void writeToXml(XmlSerializer out, String tag) throws IOException { @@ -605,6 +637,12 @@ class Owners { } out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED, String.valueOf(userRestrictionsMigrated)); + if (remoteBugreportUri != null) { + out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri); + } + if (remoteBugreportHash != null) { + out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash); + } out.endTag(null, tag); } @@ -617,12 +655,17 @@ class Owners { parser.getAttributeValue(null, ATTR_USER_RESTRICTIONS_MIGRATED); final boolean userRestrictionsMigrated = ("true".equals(userRestrictionsMigratedStr)); + final String remoteBugreportUri = parser.getAttributeValue(null, + ATTR_REMOTE_BUGREPORT_URI); + final String remoteBugreportHash = parser.getAttributeValue(null, + ATTR_REMOTE_BUGREPORT_HASH); // Has component name? If so, return [name, component] if (componentName != null) { final ComponentName admin = ComponentName.unflattenFromString(componentName); if (admin != null) { - return new OwnerInfo(name, admin, userRestrictionsMigrated); + return new OwnerInfo(name, admin, userRestrictionsMigrated, + remoteBugreportUri, remoteBugreportHash); } else { // This shouldn't happen but switch from package name -> component name // might have written bad device owner files. b/17652534 @@ -632,7 +675,8 @@ class Owners { } // Else, build with [name, package] - return new OwnerInfo(name, packageName, userRestrictionsMigrated); + return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri, + remoteBugreportHash); } public void dump(String prefix, PrintWriter pw) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java new file mode 100644 index 000000000000..806a7d99385e --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 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.server.devicepolicy; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.text.format.DateUtils; + +import com.android.internal.R; + +/** + * Utilities class for the remote bugreport operation. + */ +class RemoteBugreportUtils { + + static final int REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID = 678435657; + static final int REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID = 590907895; + + static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS; + + static final String CTL_STOP = "ctl.stop"; + static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote"; + + static final String ACTION_REMOTE_BUGREPORT_DISPATCH = + "android.intent.action.REMOTE_BUGREPORT_DISPATCH"; + static final String ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED = + "com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED"; + static final String ACTION_REMOTE_BUGREPORT_SHARING_DECLINED = + "com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED"; + static final String EXTRA_REMOTE_BUGREPORT_HASH = "android.intent.extra.REMOTE_BUGREPORT_HASH"; + + static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport"; + + static Notification buildRemoteBugreportConsentNotification(Context context) { + PendingIntent pendingIntentAccept = PendingIntent.getBroadcast( + context, REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID, + new Intent(ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED), + PendingIntent.FLAG_CANCEL_CURRENT); + PendingIntent pendingIntentDecline = PendingIntent.getBroadcast( + context, REMOTE_BUGREPORT_CONSENT_NOTIFICATION_ID, + new Intent(ACTION_REMOTE_BUGREPORT_SHARING_DECLINED), + PendingIntent.FLAG_CANCEL_CURRENT); + + return new Notification.Builder(context) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setContentTitle(context.getString( + R.string.share_remote_bugreport_notification_title)) + .setTicker(context.getString(R.string.share_remote_bugreport_notification_ticker)) + .setContentText(context.getString( + R.string.share_remote_bugreport_notification_message)) + .setStyle(new Notification.BigTextStyle().bigText(context.getString( + R.string.share_remote_bugreport_notification_message))) + .addAction(new Notification.Action.Builder(null /* icon */, + context.getString(R.string.share_remote_bugreport_notification_decline), + pendingIntentDecline).build()) + .addAction(new Notification.Action.Builder(null /* icon */, + context.getString(R.string.share_remote_bugreport_notification_accept), + pendingIntentAccept).build()) + .setOngoing(true) + .setLocalOnly(true) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setPriority(Notification.PRIORITY_MAX) + .setVibrate(new long[0]) + .build(); + } + + static Notification buildRemoteBugreportInProgressNotification(Context context, + boolean canCancelBugreport) { + Notification.Builder builder = new Notification.Builder(context) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setContentTitle(context.getString( + R.string.remote_bugreport_progress_notification_title)) + .setTicker(context.getString( + R.string.remote_bugreport_progress_notification_ticker)) + .setOngoing(true) + .setLocalOnly(true) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setPriority(Notification.PRIORITY_HIGH); + + String message = null; + if (canCancelBugreport) { + PendingIntent pendingIntentCancel = PendingIntent.getBroadcast(context, + REMOTE_BUGREPORT_IN_PROGRESS_NOTIFICATION_ID, + new Intent(ACTION_REMOTE_BUGREPORT_SHARING_DECLINED), + PendingIntent.FLAG_CANCEL_CURRENT); + message = context.getString( + R.string.remote_bugreport_progress_notification_message_can_cancel); + builder.setContentIntent(pendingIntentCancel); + } else { + message = context.getString( + R.string.remote_bugreport_progress_notification_message_cannot_cancel); + } + return builder.setContentText(message) + .setStyle(new Notification.BigTextStyle().bigText(message)) + .build(); + } +} + |