diff --git a/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl new file mode 100644 index 0000000000000..b2236c9f49072 --- /dev/null +++ b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.internal.app; + +/** + * IPC interface for an application to receive callbacks from the log access dialog callback. + */ +oneway interface ILogAccessDialogCallback { + void approveAccessForClient(int uid, String packageName); + void declineAccessForClient(int uid, String packageName); +} \ No newline at end of file diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/core/java/com/android/internal/app/LogAccessDialogActivity.java similarity index 71% rename from services/core/java/com/android/server/logcat/LogAccessDialogActivity.java rename to core/java/com/android/internal/app/LogAccessDialogActivity.java index 811e96ce6d82e..4adb8673084b7 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/core/java/com/android/internal/app/LogAccessDialogActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.logcat; +package com.android.internal.app; import android.annotation.StyleRes; import android.app.Activity; @@ -27,7 +27,14 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; +import android.text.Html; +import android.text.Spannable; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.text.style.TypefaceSpan; +import android.text.style.URLSpan; import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.InflateException; @@ -37,7 +44,6 @@ import android.widget.Button; import android.widget.TextView; import com.android.internal.R; -import com.android.server.LocalServices; /** * Dialog responsible for obtaining user consent per-use log access @@ -45,17 +51,19 @@ import com.android.server.LocalServices; public class LogAccessDialogActivity extends Activity implements View.OnClickListener { private static final String TAG = LogAccessDialogActivity.class.getSimpleName(); + public static final String EXTRA_CALLBACK = "EXTRA_CALLBACK"; + private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000; private static final int MSG_DISMISS_DIALOG = 0; - private final LogcatManagerService.LogcatManagerServiceInternal mLogcatManagerInternal = - LocalServices.getService(LogcatManagerService.LogcatManagerServiceInternal.class); - private String mPackageName; private int mUid; + private ILogAccessDialogCallback mCallback; private String mAlertTitle; + private String mAlertBody; + private String mAlertLearnMore; private AlertDialog.Builder mAlertDialog; private AlertDialog mAlert; private View mAlertView; @@ -81,6 +89,9 @@ public class LogAccessDialogActivity extends Activity implements return; } + mAlertBody = getResources().getString(R.string.log_access_confirmation_body); + mAlertLearnMore = getResources().getString(R.string.log_access_confirmation_learn_more); + // create View boolean isDarkTheme = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -118,6 +129,13 @@ public class LogAccessDialogActivity extends Activity implements return false; } + mCallback = ILogAccessDialogCallback.Stub.asInterface( + intent.getExtras().getBinder(EXTRA_CALLBACK)); + if (mCallback == null) { + Slog.e(TAG, "Missing callback"); + return false; + } + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); if (mPackageName == null || mPackageName.length() == 0) { Slog.e(TAG, "Missing package name extra"); @@ -165,13 +183,22 @@ public class LogAccessDialogActivity extends Activity implements return titleString; } + private Spannable styleFont(String text) { + Spannable s = (Spannable) Html.fromHtml(text); + for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) { + TypefaceSpan typefaceSpan = new TypefaceSpan("google-sans"); + s.setSpan(typefaceSpan, s.getSpanStart(span), s.getSpanEnd(span), 0); + } + return s; + } + /** * Returns the dialog view. * If we cannot retrieve the package name, it returns null and we decline the full device log * access */ private View createView(@StyleRes int themeId) { - Context themedContext = new ContextThemeWrapper(getApplicationContext(), themeId); + Context themedContext = new ContextThemeWrapper(this, themeId); final View view = LayoutInflater.from(themedContext).inflate( R.layout.log_access_user_consent_dialog_permission, null /*root*/); @@ -182,6 +209,19 @@ public class LogAccessDialogActivity extends Activity implements ((TextView) view.findViewById(R.id.log_access_dialog_title)) .setText(mAlertTitle); + if (!TextUtils.isEmpty(mAlertLearnMore)) { + Spannable mSpannableLearnMore = styleFont(mAlertLearnMore); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(TextUtils.concat(mAlertBody, "\n\n", mSpannableLearnMore)); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setMovementMethod(LinkMovementMethod.getInstance()); + } else { + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(mAlertBody); + } + Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button); button_allow.setOnClickListener(this); @@ -194,19 +234,27 @@ public class LogAccessDialogActivity extends Activity implements @Override public void onClick(View view) { - switch (view.getId()) { - case R.id.log_access_dialog_allow_button: - mLogcatManagerInternal.approveAccessForClient(mUid, mPackageName); - finish(); - break; - case R.id.log_access_dialog_deny_button: - declineLogAccess(); - finish(); - break; + try { + switch (view.getId()) { + case R.id.log_access_dialog_allow_button: + mCallback.approveAccessForClient(mUid, mPackageName); + finish(); + break; + case R.id.log_access_dialog_deny_button: + declineLogAccess(); + finish(); + break; + } + } catch (RemoteException e) { + finish(); } } private void declineLogAccess() { - mLogcatManagerInternal.declineAccessForClient(mUid, mPackageName); + try { + mCallback.declineAccessForClient(mUid, mPackageName); + } catch (RemoteException e) { + finish(); + } } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b91bd1898a4cd..5ae133bbe6e61 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6783,8 +6783,9 @@ android:exported="false"> - diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6ec98e82211c7..5763345aba4d5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5755,10 +5755,21 @@ Don\u2019t allow - Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device. + + Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs. + + + + <a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a> + + + + Don\u2019t show again diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 546bb21df2a6a..6e574bd788a9c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3925,8 +3925,10 @@ + + diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index 1bcc21e66302b..909896693bea9 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -38,6 +38,8 @@ import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ILogAccessDialogCallback; +import com.android.internal.app.LogAccessDialogActivity; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -98,7 +100,7 @@ public final class LogcatManagerService extends SystemService { private final Injector mInjector; private final Supplier mClock; private final BinderService mBinderService; - private final LogcatManagerServiceInternal mLocalService; + private final LogAccessDialogCallback mDialogCallback; private final Handler mHandler; private ActivityManagerInternal mActivityManagerInternal; private ILogd mLogdService; @@ -203,7 +205,8 @@ public final class LogcatManagerService extends SystemService { } } - final class LogcatManagerServiceInternal { + final class LogAccessDialogCallback extends ILogAccessDialogCallback.Stub { + @Override public void approveAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -213,6 +216,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(msg, mClock.get()); } + @Override public void declineAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -299,7 +303,7 @@ public final class LogcatManagerService extends SystemService { mInjector = injector; mClock = injector.createClock(); mBinderService = new BinderService(); - mLocalService = new LogcatManagerServiceInternal(); + mDialogCallback = new LogAccessDialogCallback(); mHandler = new LogAccessRequestHandler(injector.getLooper(), this); } @@ -308,15 +312,14 @@ public final class LogcatManagerService extends SystemService { try { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); publishBinderService("logcat", mBinderService); - publishLocalService(LogcatManagerServiceInternal.class, mLocalService); } catch (Throwable t) { Slog.e(TAG, "Could not start the LogcatManagerService.", t); } } @VisibleForTesting - LogcatManagerServiceInternal getLocalService() { - return mLocalService; + LogAccessDialogCallback getDialogCallback() { + return mDialogCallback; } @VisibleForTesting @@ -430,6 +433,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PENDING_TIMEOUT, client), mClock.get() + PENDING_CONFIRMATION_TIMEOUT_MILLIS); final Intent mIntent = createIntent(client); + mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); } @@ -530,6 +534,7 @@ public final class LogcatManagerService extends SystemService { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, client.mPackageName); intent.putExtra(Intent.EXTRA_UID, client.mUid); + intent.putExtra(LogAccessDialogActivity.EXTRA_CALLBACK, mDialogCallback.asBinder()); return intent; } diff --git a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java index f33001774263e..429edcaadc3c1 100644 --- a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java @@ -72,7 +72,7 @@ public class LogcatManagerServiceTest { private ILogd mLogdMock; private LogcatManagerService mService; - private LogcatManagerService.LogcatManagerServiceInternal mLocalService; + private LogcatManagerService.LogAccessDialogCallback mDialogCallback; private ContextWrapper mContextSpy; private OffsettableClock mClock; private TestLooper mTestLooper; @@ -106,7 +106,7 @@ public class LogcatManagerServiceTest { return mLogdMock; } }); - mLocalService = mService.getLocalService(); + mDialogCallback = mService.getDialogCallback(); mService.onStart(); } @@ -181,7 +181,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -196,7 +196,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -214,7 +214,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -234,7 +234,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -249,7 +249,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -267,7 +267,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -287,7 +287,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP2_UID, APP2_GID, APP2_PID, FD2); @@ -304,7 +304,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); advanceTime(LogcatManagerService.STATUS_EXPIRATION_TIMEOUT_MILLIS);