Merge "Extract bugreport collection from DPMS"

This commit is contained in:
TreeHugger Robot
2020-08-20 11:50:12 +00:00
committed by Android (Google) Code Review
3 changed files with 361 additions and 340 deletions

View File

@@ -302,7 +302,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -322,7 +321,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -575,13 +573,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private final CertificateMonitor mCertificateMonitor;
private final SecurityLogMonitor mSecurityLogMonitor;
private final RemoteBugreportManager mBugreportCollectionManager;
@GuardedBy("getLockObject()")
private NetworkLogger mNetworkLogger;
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
private final SetupContentObserver mSetupContentObserver;
private final DevicePolicyConstantsObserver mConstantsObserver;
@@ -633,44 +629,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@VisibleForTesting
final TransferOwnershipMetadataManager mTransferOwnershipMetadataManager;
private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() {
@Override
public void run() {
if(mRemoteBugreportServiceIsActive.get()) {
onBugreportFailed();
}
}
};
/** Listens only if mHasFeature == true. */
private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
&& mRemoteBugreportServiceIsActive.get()) {
onBugreportFinished(intent);
}
}
};
/** Listens only if mHasFeature == true. */
private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
onBugreportSharingAccepted();
} else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
onBugreportSharingDeclined();
}
mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
}
};
public static final class Lifecycle extends SystemService {
private BaseIDevicePolicyManager mService;
@@ -748,17 +706,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
UserHandle.ALL);
&& userHandle == mOwners.getDeviceOwnerUserId()) {
mBugreportCollectionManager.checkForPendingBugreportAfterBoot();
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -1435,10 +1385,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
mDeviceAdminServiceController = new DeviceAdminServiceController(this, mConstants);
mOverlayPackagesProvider = new OverlayPackagesProvider(mContext);
mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
if (!mHasFeature) {
// Skip the rest of the initialization
@@ -6867,45 +6816,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(isDeviceOwner(identity));
ensureAllUsersAffiliated();
if (mRemoteBugreportServiceIsActive.get()
|| (getDeviceOwnerRemoteBugreportUri() != null)) {
Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
return false;
}
final long currentTime = System.currentTimeMillis();
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
if (currentTime > policyData.mLastBugReportRequestTime) {
policyData.mLastBugReportRequestTime = currentTime;
saveSettingsLocked(UserHandle.USER_SYSTEM);
}
}
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
mInjector.getIActivityManager().requestRemoteBugReport();
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
if (mBugreportCollectionManager.requestBugreport()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.REQUEST_BUGREPORT)
.setAdmin(who)
.write();
final long currentTime = System.currentTimeMillis();
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
if (currentTime > policyData.mLastBugReportRequestTime) {
policyData.mLastBugReportRequestTime = currentTime;
saveSettingsLocked(UserHandle.USER_SYSTEM);
}
}
return true;
} catch (RemoteException re) {
// should never happen
Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
} else {
return false;
} finally {
mInjector.binderRestoreCallingIdentity(callingIdentity);
}
}
@@ -6949,146 +6877,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
private String getDeviceOwnerRemoteBugreportUri() {
void sendBugreportToDeviceOwner(Uri bugreportUri, String bugreportHash) {
synchronized (getLockObject()) {
return mOwners.getDeviceOwnerRemoteBugreportUri();
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE);
intent.setComponent(mOwners.getDeviceOwnerComponent());
intent.setDataAndType(bugreportUri, RemoteBugreportManager.BUGREPORT_MIMETYPE);
intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final UriGrantsManagerInternal ugm = LocalServices
.getService(UriGrantsManagerInternal.class);
final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
mOwners.getDeviceOwnerUserId());
ugm.grantUriPermissionUncheckedFromIntent(needed, null);
mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
}
}
private void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri,
String bugreportHash) {
void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri, String bugreportHash) {
synchronized (getLockObject()) {
mOwners.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUri, bugreportHash);
}
}
private void registerRemoteBugreportReceivers() {
try {
IntentFilter filterFinished = new IntentFilter(
DevicePolicyManager.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(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
}
private void onBugreportFinished(Intent intent) {
mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
mRemoteBugreportServiceIsActive.set(false);
Uri bugreportUri = intent.getData();
String bugreportUriString = null;
if (bugreportUri != null) {
bugreportUriString = bugreportUri.toString();
}
String bugreportHash = intent.getStringExtra(
DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
if (mRemoteBugreportSharingAccepted.get()) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
} else {
setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
UserHandle.ALL);
}
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.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;
Pair<String, String> getDeviceOwnerRemoteBugreportUriAndHash() {
synchronized (getLockObject()) {
bugreportUriString = getDeviceOwnerRemoteBugreportUri();
bugreportHash = mOwners.getDeviceOwnerRemoteBugreportHash();
}
if (bugreportUriString != null) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
} else if (mRemoteBugreportServiceIsActive.get()) {
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
UserHandle.ALL);
}
}
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);
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 (getLockObject()) {
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);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final UriGrantsManagerInternal ugm = LocalServices
.getService(UriGrantsManagerInternal.class);
final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
mOwners.getDeviceOwnerUserId());
ugm.grantUriPermissionUncheckedFromIntent(needed, null);
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);
final String uri = mOwners.getDeviceOwnerRemoteBugreportUri();
return uri == null ? null
: new Pair<>(uri, mOwners.getDeviceOwnerRemoteBugreportHash());
}
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright (C) 2020 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 static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED;
import static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED;
import static android.app.admin.DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH;
import static android.app.admin.DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE;
import static android.app.admin.DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.admin.DeviceAdminReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Class managing bugreport collection upon device owner's request.
*/
public class RemoteBugreportManager {
private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
private static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
private static final String CTL_STOP = "ctl.stop";
private static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
private static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
NOTIFICATION_BUGREPORT_STARTED,
NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
})
@interface RemoteBugreportNotificationType {}
private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
private final Context mContext;
private final Handler mHandler;
private final Runnable mRemoteBugreportTimeoutRunnable = () -> {
if (mRemoteBugreportServiceIsActive.get()) {
onBugreportFailed();
}
};
private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (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) {
final String action = intent.getAction();
mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
if (ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
onBugreportSharingAccepted();
} else if (ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
onBugreportSharingDeclined();
}
mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
}
};
public RemoteBugreportManager(
DevicePolicyManagerService service, DevicePolicyManagerService.Injector injector) {
mService = service;
mInjector = injector;
mContext = service.mContext;
mHandler = service.mHandler;
}
private Notification buildNotification(@RemoteBugreportNotificationType int type) {
final Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
dialogIntent.putExtra(EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
// Fill the component explicitly to prevent the PendingIntent from being intercepted
// and fired with crafted target. b/155183624
final ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
mContext.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
if (targetInfo != null) {
dialogIntent.setComponent(targetInfo.getComponentName());
} else {
Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
}
final PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(mContext, type,
dialogIntent, 0, null, UserHandle.CURRENT);
final Notification.Builder builder =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setOngoing(true)
.setLocalOnly(true)
.setContentIntent(pendingDialogIntent)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color));
if (type == NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
builder.setContentTitle(mContext.getString(
R.string.sharing_remote_bugreport_notification_title))
.setProgress(0, 0, true);
} else if (type == NOTIFICATION_BUGREPORT_STARTED) {
builder.setContentTitle(mContext.getString(
R.string.taking_remote_bugreport_notification_title))
.setProgress(0, 0, true);
} else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
final PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(mContext,
NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_ACCEPTED),
PendingIntent.FLAG_CANCEL_CURRENT);
final PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(mContext,
NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_DECLINED),
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
R.string.share_remote_bugreport_action), pendingIntentAccept).build())
.setContentTitle(mContext.getString(
R.string.share_remote_bugreport_notification_title))
.setContentText(mContext.getString(
R.string.share_remote_bugreport_notification_message_finished))
.setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
R.string.share_remote_bugreport_notification_message_finished)));
}
return builder.build();
}
/**
* Initiates bugreport collection.
* @return whether collection was initiated successfully.
*/
public boolean requestBugreport() {
if (mRemoteBugreportServiceIsActive.get()
|| (mService.getDeviceOwnerRemoteBugreportUriAndHash() != null)) {
Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
return false;
}
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
mInjector.getIActivityManager().requestRemoteBugReport();
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
buildNotification(NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable, 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 void registerRemoteBugreportReceivers() {
try {
final IntentFilter filterFinished =
new IntentFilter(ACTION_REMOTE_BUGREPORT_DISPATCH, 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 " + BUGREPORT_MIMETYPE, e);
}
final IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
}
private void onBugreportFinished(Intent intent) {
mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
mRemoteBugreportServiceIsActive.set(false);
final Uri bugreportUri = intent.getData();
String bugreportUriString = null;
if (bugreportUri != null) {
bugreportUriString = bugreportUri.toString();
}
final String bugreportHash = intent.getStringExtra(EXTRA_REMOTE_BUGREPORT_HASH);
if (mRemoteBugreportSharingAccepted.get()) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().cancel(LOG_TAG,
NOTIFICATION_ID);
} else {
mService.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
UserHandle.ALL);
}
mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
}
private void onBugreportFailed() {
mRemoteBugreportServiceIsActive.set(false);
mInjector.systemPropertiesSet(CTL_STOP, REMOTE_BUGREPORT_SERVICE);
mRemoteBugreportSharingAccepted.set(false);
mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
final Bundle extras = new Bundle();
extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING);
mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
}
private void onBugreportSharingAccepted() {
mRemoteBugreportSharingAccepted.set(true);
final Pair<String, String> uriAndHash = mService.getDeviceOwnerRemoteBugreportUriAndHash();
if (uriAndHash != null) {
shareBugreportWithDeviceOwnerIfExists(uriAndHash.first, uriAndHash.second);
} else if (mRemoteBugreportServiceIsActive.get()) {
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
buildNotification(NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
UserHandle.ALL);
}
}
private void onBugreportSharingDeclined() {
if (mRemoteBugreportServiceIsActive.get()) {
mInjector.systemPropertiesSet(CTL_STOP,
REMOTE_BUGREPORT_SERVICE);
mRemoteBugreportServiceIsActive.set(false);
mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
}
mRemoteBugreportSharingAccepted.set(false);
mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
mService.sendDeviceOwnerCommand(
DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null);
}
private void shareBugreportWithDeviceOwnerIfExists(
String bugreportUriString, String bugreportHash) {
try {
if (bugreportUriString == null) {
throw new FileNotFoundException();
}
final Uri bugreportUri = Uri.parse(bugreportUriString);
mService.sendBugreportToDeviceOwner(bugreportUri, bugreportHash);
} catch (FileNotFoundException e) {
final Bundle extras = new Bundle();
extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE);
mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
} finally {
mRemoteBugreportSharingAccepted.set(false);
mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
}
}
/**
* Check if a bugreport was collected but not shared before reboot because the user didn't act
* upon sharing notification.
*/
public void checkForPendingBugreportAfterBoot() {
if (mService.getDeviceOwnerRemoteBugreportUriAndHash() == null) {
return;
}
final IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED), UserHandle.ALL);
}
}

View File

@@ -1,121 +0,0 @@
/*
* 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.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Utilities class for the remote bugreport operation.
*/
class RemoteBugreportUtils {
private static final String TAG = "RemoteBugreportUtils";
static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED,
DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
})
@interface RemoteBugreportNotificationType {}
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 = "bugreportd";
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
static Notification buildNotification(Context context,
@RemoteBugreportNotificationType int type) {
Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
dialogIntent.putExtra(DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
// Fill the component explicitly to prevent the PendingIntent from being intercepted
// and fired with crafted target. b/155183624
ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
context.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
if (targetInfo != null) {
dialogIntent.setComponent(targetInfo.getComponentName());
} else {
Slog.wtf(TAG, "Failed to resolve intent for remote bugreport dialog");
}
PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type,
dialogIntent, 0, null, UserHandle.CURRENT);
Notification.Builder builder =
new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setOngoing(true)
.setLocalOnly(true)
.setContentIntent(pendingDialogIntent)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
builder.setContentTitle(context.getString(
R.string.sharing_remote_bugreport_notification_title))
.setProgress(0, 0, true);
} else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED) {
builder.setContentTitle(context.getString(
R.string.taking_remote_bugreport_notification_title))
.setProgress(0, 0, true);
} else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
new Intent(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED),
PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(context,
NOTIFICATION_ID, new Intent(
DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED),
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
R.string.share_remote_bugreport_action), pendingIntentAccept).build())
.setContentTitle(context.getString(
R.string.share_remote_bugreport_notification_title))
.setContentText(context.getString(
R.string.share_remote_bugreport_notification_message_finished))
.setStyle(new Notification.BigTextStyle().bigText(context.getString(
R.string.share_remote_bugreport_notification_message_finished)));
}
return builder.build();
}
}