| /* |
| * 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.systemui.logcat; |
| |
| import android.annotation.StyleRes; |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.text.SpannableString; |
| import android.text.Spanned; |
| import android.text.TextUtils; |
| import android.text.method.LinkMovementMethod; |
| import android.text.style.URLSpan; |
| import android.util.Slog; |
| import android.view.ContextThemeWrapper; |
| import android.view.InflateException; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.TextView; |
| |
| import com.android.internal.app.ILogAccessDialogCallback; |
| import com.android.systemui.R; |
| |
| |
| /** |
| * Dialog responsible for obtaining user consent per-use log access |
| */ |
| 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 String mPackageName; |
| private int mUid; |
| private ILogAccessDialogCallback mCallback; |
| |
| private String mAlertTitle; |
| private String mAlertBody; |
| |
| private boolean mAlertLearnMoreLink; |
| private SpannableString mAlertLearnMore; |
| |
| private AlertDialog.Builder mAlertDialog; |
| private AlertDialog mAlert; |
| private View mAlertView; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // retrieve Intent extra information |
| if (!readIntentInfo(getIntent())) { |
| Slog.e(TAG, "Invalid Intent extras, finishing"); |
| finish(); |
| return; |
| } |
| |
| // retrieve the title string from passed intent extra |
| try { |
| mAlertTitle = getTitleString(this, mPackageName, mUid); |
| } catch (NameNotFoundException e) { |
| Slog.e(TAG, "Unable to fetch label of package " + mPackageName, e); |
| declineLogAccess(); |
| finish(); |
| return; |
| } |
| |
| mAlertBody = getString(R.string.log_access_confirmation_body); |
| mAlertLearnMoreLink = this.getResources() |
| .getBoolean(R.bool.log_access_confirmation_learn_more_as_link); |
| if (mAlertLearnMoreLink) { |
| mAlertLearnMore = new SpannableString( |
| getString(R.string.log_access_confirmation_learn_more)); |
| mAlertLearnMore.setSpan(new URLSpan( |
| getString(R.string.log_access_confirmation_learn_more_url)), |
| 0, mAlertLearnMore.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); |
| } else { |
| mAlertLearnMore = new SpannableString( |
| getString(R.string.log_access_confirmation_learn_more_at, |
| getString(R.string.log_access_confirmation_learn_more_url))); |
| } |
| |
| // create View |
| int themeId = R.style.LogAccessDialogTheme; |
| mAlertView = createView(themeId); |
| |
| // create AlertDialog |
| mAlertDialog = new AlertDialog.Builder(this, themeId); |
| mAlertDialog.setView(mAlertView); |
| mAlertDialog.setOnCancelListener(dialog -> declineLogAccess()); |
| mAlertDialog.setOnDismissListener(dialog -> finish()); |
| |
| // show Alert |
| mAlert = mAlertDialog.create(); |
| mAlert.getWindow().setHideOverlayWindows(true); |
| mAlert.show(); |
| |
| // set Alert Timeout |
| mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| if (!isChangingConfigurations() && mAlert != null && mAlert.isShowing()) { |
| mAlert.dismiss(); |
| } |
| mAlert = null; |
| } |
| |
| private boolean readIntentInfo(Intent intent) { |
| if (intent == null) { |
| Slog.e(TAG, "Intent is null"); |
| 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"); |
| return false; |
| } |
| |
| if (!intent.hasExtra(Intent.EXTRA_UID)) { |
| Slog.e(TAG, "Missing EXTRA_UID"); |
| return false; |
| } |
| |
| mUid = intent.getIntExtra(Intent.EXTRA_UID, 0); |
| |
| return true; |
| } |
| |
| private Handler mHandler = new Handler() { |
| public void handleMessage(android.os.Message msg) { |
| switch (msg.what) { |
| case MSG_DISMISS_DIALOG: |
| if (mAlert != null) { |
| mAlert.dismiss(); |
| mAlert = null; |
| declineLogAccess(); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| }; |
| |
| private String getTitleString(Context context, String callingPackage, int uid) |
| throws NameNotFoundException { |
| PackageManager pm = context.getPackageManager(); |
| |
| CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, |
| PackageManager.MATCH_DIRECT_BOOT_AUTO, |
| UserHandle.getUserId(uid)).loadLabel(pm); |
| |
| String titleString = context.getString(R.string.log_access_confirmation_title, appLabel); |
| |
| return titleString; |
| } |
| |
| /** |
| * 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(this, themeId); |
| final View view = LayoutInflater.from(themedContext).inflate( |
| R.layout.log_access_user_consent_dialog_permission, null /*root*/); |
| |
| if (view == null) { |
| throw new InflateException(); |
| } |
| |
| ((TextView) view.findViewById(R.id.log_access_dialog_title)) |
| .setText(mAlertTitle); |
| |
| if (!TextUtils.isEmpty(mAlertLearnMore)) { |
| ((TextView) view.findViewById(R.id.log_access_dialog_body)) |
| .setText(TextUtils.concat(mAlertBody, "\n\n", mAlertLearnMore)); |
| if (mAlertLearnMoreLink) { |
| ((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); |
| |
| Button button_deny = (Button) view.findViewById(R.id.log_access_dialog_deny_button); |
| button_deny.setOnClickListener(this); |
| |
| return view; |
| |
| } |
| |
| @Override |
| public void onClick(View view) { |
| try { |
| if (view.getId() == R.id.log_access_dialog_allow_button) { |
| mCallback.approveAccessForClient(mUid, mPackageName); |
| finish(); |
| } else if (view.getId() == R.id.log_access_dialog_deny_button) { |
| declineLogAccess(); |
| finish(); |
| } |
| } catch (RemoteException e) { |
| finish(); |
| } |
| } |
| |
| private void declineLogAccess() { |
| try { |
| mCallback.declineAccessForClient(mUid, mPackageName); |
| } catch (RemoteException e) { |
| finish(); |
| } |
| } |
| } |