Merge "Add hyperlink to "Learn more" in device log access dialog" into tm-qpr-dev

This commit is contained in:
Eun-Jeong Shin
2022-09-24 00:17:45 +00:00
committed by Android (Google) Code Review
7 changed files with 126 additions and 34 deletions

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -6783,8 +6783,9 @@
android:exported="false">
</activity>
<activity android:name="com.android.server.logcat.LogAccessDialogActivity"
<activity android:name="com.android.internal.app.LogAccessDialogActivity"
android:theme="@style/Theme.Translucent.NoTitleBar"
android:process=":ui"
android:excludeFromRecents="true"
android:exported="false">
</activity>

View File

@@ -5755,10 +5755,21 @@
<string name="log_access_confirmation_deny">Don\u2019t allow</string>
<!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
<string name="log_access_confirmation_body">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.
<string name="log_access_confirmation_body" product="default">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.
</string>
<!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
<string name="log_access_confirmation_body" product="tv">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.
</string>
<!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
<string name="log_access_confirmation_learn_more" product="default" translatable="false">&lt;a href="https://support.google.com/android?p=system_logs#topic=7313011"&gt;Learn more&lt;/a&gt;</string>
<!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
<string name="log_access_confirmation_learn_more" product="tv" translatable="false"></string>
<!-- Privacy notice do not show [CHAR LIMIT=20] -->
<string name="log_access_do_not_show_again">Don\u2019t show again</string>

View File

@@ -3925,8 +3925,10 @@
<java-symbol type="string" name="log_access_confirmation_deny" />
<java-symbol type="string" name="log_access_confirmation_title" />
<java-symbol type="string" name="log_access_confirmation_body" />
<java-symbol type="string" name="log_access_confirmation_learn_more" />
<java-symbol type="layout" name="log_access_user_consent_dialog_permission" />
<java-symbol type="id" name="log_access_dialog_title" />
<java-symbol type="id" name="log_access_dialog_body" />
<java-symbol type="id" name="log_access_dialog_allow_button" />
<java-symbol type="id" name="log_access_dialog_deny_button" />

View File

@@ -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<Long> 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;
}

View File

@@ -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);