Loading packages/Shell/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -641,6 +641,15 @@ </intent-filter> </receiver> <receiver android:name=".ProfcollectUploadReceiver" android:exported="true" android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD" > <intent-filter> <action android:name="com.android.shell.action.UPLOAD_REPORT" /> </intent-filter> </receiver> <service android:name=".BugreportProgressService" android:exported="false"/> Loading packages/Shell/res/xml/file_provider_paths.xml +1 −0 Original line number Diff line number Diff line <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="bugreports" path="bugreports/" /> <root-path name="profcollect" path="/data/misc/profcollectd/report/" /> </paths> packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.shell; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.util.Log; import androidx.core.content.FileProvider; import com.android.internal.R; import java.io.File; import java.util.List; /** * A proxy service that relays report upload requests to the uploader app, while translating * the path to the report to a content URI owned by this service. */ public final class ProfcollectUploadReceiver extends BroadcastReceiver { private static final String AUTHORITY = "com.android.shell"; private static final String PROFCOLLECT_DATA_ROOT = "/data/misc/profcollectd/report/"; private static final String LOG_TAG = "ProfcollectUploadReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.i(LOG_TAG, "Received upload intent"); String uploaderPkg = getUploaderPackageName(context); String uploaderAction = getUploaderActionName(context); try { ApplicationInfo info = context.getPackageManager().getApplicationInfo(uploaderPkg, 0); if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { Log.e(LOG_TAG, "The profcollect uploader app " + uploaderPkg + " must be a system application"); return; } } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Cannot find profcollect uploader app " + uploaderPkg); return; } String filename = intent.getStringExtra("filename"); File reportFile = new File(PROFCOLLECT_DATA_ROOT + filename); Uri reportUri = FileProvider.getUriForFile(context, AUTHORITY, reportFile); Intent uploadIntent = new Intent(uploaderAction) .setPackage(uploaderPkg) .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") .putExtra("EXTRA_PACKAGE_NAME", context.getPackageName()) .setData(reportUri) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); if (receivers == null || receivers.isEmpty()) { Log.e(LOG_TAG, "No one to receive upload intent, abort upload."); return; } context.grantUriPermission(uploaderPkg, reportUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); context.sendBroadcast(uploadIntent); } private String getUploaderPackageName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderApp); } private String getUploaderActionName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderAction); } } services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +13 −71 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; Loading @@ -32,8 +31,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.util.Log; Loading @@ -45,9 +42,6 @@ import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; Loading Loading @@ -306,79 +300,27 @@ public final class ProfcollectForwardingService extends SystemService { return; } if (!getUploaderEnabledConfig(getContext())) { return; } Context context = getContext(); new Thread(() -> { try { Context context = getContext(); final String uploaderPkg = getUploaderPackageName(context); final String uploaderAction = getUploaderActionName(context); String reportUuid = mIProfcollect.report(); final int profileId = getBBProfileId(); String reportDir = "/data/user/" + profileId + "/com.google.android.apps.internal.betterbug/cache/"; String reportPath = reportDir + reportUuid + ".zip"; // Prepare profile report String reportName = mIProfcollect.report() + ".zip"; if (!Files.exists(Paths.get(reportDir))) { Log.i(LOG_TAG, "Destination directory does not exist, abort upload."); if (!context.getResources().getBoolean( R.bool.config_profcollectReportUploaderEnabled)) { Log.i(LOG_TAG, "Upload is not enabled."); return; } Intent uploadIntent = new Intent(uploaderAction) .setPackage(uploaderPkg) .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName()) .putExtra("EXTRA_PROFILE_PATH", reportPath) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); if (receivers == null || receivers.isEmpty()) { Log.i(LOG_TAG, "No one to receive upload intent, abort upload."); return; } mIProfcollect.copy_report_to_bb(profileId, reportUuid); context.sendBroadcast(uploadIntent); mIProfcollect.delete_report(reportUuid); // Upload the report Intent intent = new Intent() .setPackage("com.android.shell") .setAction("com.android.shell.action.UPLOAD_REPORT") .putExtra("filename", reportName); context.sendBroadcast(intent); } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage()); } }).start(); } /** * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system * user ID. * * @return BetterBug's profile ID. */ private int getBBProfileId() { UserManager userManager = UserManager.get(getContext()); int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false); for (int p : profiles) { if (userManager.getUserInfo(p).isManagedProfile()) { return p; } } return UserHandle.USER_SYSTEM; } private boolean getUploaderEnabledConfig(Context context) { return context.getResources().getBoolean( R.bool.config_profcollectReportUploaderEnabled); } private String getUploaderPackageName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderApp); } private String getUploaderActionName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderAction); } } Loading
packages/Shell/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -641,6 +641,15 @@ </intent-filter> </receiver> <receiver android:name=".ProfcollectUploadReceiver" android:exported="true" android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD" > <intent-filter> <action android:name="com.android.shell.action.UPLOAD_REPORT" /> </intent-filter> </receiver> <service android:name=".BugreportProgressService" android:exported="false"/> Loading
packages/Shell/res/xml/file_provider_paths.xml +1 −0 Original line number Diff line number Diff line <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="bugreports" path="bugreports/" /> <root-path name="profcollect" path="/data/misc/profcollectd/report/" /> </paths>
packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.shell; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.util.Log; import androidx.core.content.FileProvider; import com.android.internal.R; import java.io.File; import java.util.List; /** * A proxy service that relays report upload requests to the uploader app, while translating * the path to the report to a content URI owned by this service. */ public final class ProfcollectUploadReceiver extends BroadcastReceiver { private static final String AUTHORITY = "com.android.shell"; private static final String PROFCOLLECT_DATA_ROOT = "/data/misc/profcollectd/report/"; private static final String LOG_TAG = "ProfcollectUploadReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.i(LOG_TAG, "Received upload intent"); String uploaderPkg = getUploaderPackageName(context); String uploaderAction = getUploaderActionName(context); try { ApplicationInfo info = context.getPackageManager().getApplicationInfo(uploaderPkg, 0); if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { Log.e(LOG_TAG, "The profcollect uploader app " + uploaderPkg + " must be a system application"); return; } } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Cannot find profcollect uploader app " + uploaderPkg); return; } String filename = intent.getStringExtra("filename"); File reportFile = new File(PROFCOLLECT_DATA_ROOT + filename); Uri reportUri = FileProvider.getUriForFile(context, AUTHORITY, reportFile); Intent uploadIntent = new Intent(uploaderAction) .setPackage(uploaderPkg) .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") .putExtra("EXTRA_PACKAGE_NAME", context.getPackageName()) .setData(reportUri) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); if (receivers == null || receivers.isEmpty()) { Log.e(LOG_TAG, "No one to receive upload intent, abort upload."); return; } context.grantUriPermission(uploaderPkg, reportUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); context.sendBroadcast(uploadIntent); } private String getUploaderPackageName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderApp); } private String getUploaderActionName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderAction); } }
services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +13 −71 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; Loading @@ -32,8 +31,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.util.Log; Loading @@ -45,9 +42,6 @@ import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; Loading Loading @@ -306,79 +300,27 @@ public final class ProfcollectForwardingService extends SystemService { return; } if (!getUploaderEnabledConfig(getContext())) { return; } Context context = getContext(); new Thread(() -> { try { Context context = getContext(); final String uploaderPkg = getUploaderPackageName(context); final String uploaderAction = getUploaderActionName(context); String reportUuid = mIProfcollect.report(); final int profileId = getBBProfileId(); String reportDir = "/data/user/" + profileId + "/com.google.android.apps.internal.betterbug/cache/"; String reportPath = reportDir + reportUuid + ".zip"; // Prepare profile report String reportName = mIProfcollect.report() + ".zip"; if (!Files.exists(Paths.get(reportDir))) { Log.i(LOG_TAG, "Destination directory does not exist, abort upload."); if (!context.getResources().getBoolean( R.bool.config_profcollectReportUploaderEnabled)) { Log.i(LOG_TAG, "Upload is not enabled."); return; } Intent uploadIntent = new Intent(uploaderAction) .setPackage(uploaderPkg) .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName()) .putExtra("EXTRA_PROFILE_PATH", reportPath) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); if (receivers == null || receivers.isEmpty()) { Log.i(LOG_TAG, "No one to receive upload intent, abort upload."); return; } mIProfcollect.copy_report_to_bb(profileId, reportUuid); context.sendBroadcast(uploadIntent); mIProfcollect.delete_report(reportUuid); // Upload the report Intent intent = new Intent() .setPackage("com.android.shell") .setAction("com.android.shell.action.UPLOAD_REPORT") .putExtra("filename", reportName); context.sendBroadcast(intent); } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage()); } }).start(); } /** * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system * user ID. * * @return BetterBug's profile ID. */ private int getBBProfileId() { UserManager userManager = UserManager.get(getContext()); int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false); for (int p : profiles) { if (userManager.getUserInfo(p).isManagedProfile()) { return p; } } return UserHandle.USER_SYSTEM; } private boolean getUploaderEnabledConfig(Context context) { return context.getResources().getBoolean( R.bool.config_profcollectReportUploaderEnabled); } private String getUploaderPackageName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderApp); } private String getUploaderActionName(Context context) { return context.getResources().getString( R.string.config_defaultProfcollectReportUploaderAction); } }