1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.phone; 18 19 import android.app.Activity; 20 import android.app.ActivityOptions; 21 import android.app.AlertDialog; 22 import android.app.role.RoleManager; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.UserInfo; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Log; 33 34 import java.util.List; 35 36 /** Used to display an error dialog from Telephony service. */ 37 public class ErrorDialogActivity extends Activity { 38 39 private static final String TAG = ErrorDialogActivity.class.getSimpleName(); 40 41 @Override onCreate(Bundle savedInstanceState)42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 getWindow() 45 .addSystemFlags( 46 android.view.WindowManager.LayoutParams 47 .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 48 showDialog(); 49 } 50 51 @Override finish()52 public void finish() { 53 super.finish(); 54 // Don't show the return to previous task animation to avoid showing a black screen. 55 // Just dismiss the dialog and undim the previous activity immediately. 56 overridePendingTransition(0, 0); 57 } 58 showDialog()59 private void showDialog() { 60 int managedProfileUserId = 61 getManagedProfileUserId( 62 getApplicationContext(), getApplicationContext().getUserId()); 63 if (managedProfileUserId == UserHandle.USER_NULL) { 64 Log.w(TAG, "Error dialog is only applicable to managed profile."); 65 finish(); 66 } 67 String defaultMessagesAppPackage = 68 getBaseContext() 69 .getSystemService(RoleManager.class) 70 .getSmsRoleHolder(managedProfileUserId); 71 72 Intent smsIntent = new Intent(Intent.ACTION_SENDTO) 73 .addCategory(Intent.CATEGORY_DEFAULT) 74 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 75 .setData(Uri.parse("smsto:")); 76 Intent marketIntent = 77 new Intent( 78 Intent.ACTION_VIEW, 79 Uri.parse("market://search?q=messages")); 80 int positiveButtonText = 0; 81 Intent intent = null; 82 boolean showPositiveActionButton = true; 83 // A messages app may not be available in the managed profile. We try to handle that 84 // gracefully by redirecting to install a suitable app. 85 // Failing that, we simply omit the positive action button as the user has no mechanism 86 // to send the message. 87 if (defaultMessagesAppPackage != null 88 || canStartActivityAsUser( 89 smsIntent, 90 managedProfileUserId)) { 91 positiveButtonText = R.string.send_from_work_profile_action_str; 92 intent = smsIntent; 93 } else if (canStartActivityAsUser(marketIntent, managedProfileUserId)) { 94 positiveButtonText = R.string.install_messages_on_work_profile_action_str; 95 intent = marketIntent; 96 } else { 97 showPositiveActionButton = false; 98 } 99 100 // Variable has to be effectively final to be passing into the lambda, so copying it 101 // here. 102 Intent finalIntent = intent; 103 final DialogInterface.OnClickListener listener = 104 new DialogInterface.OnClickListener() { 105 @Override 106 public void onClick(DialogInterface dialog, int which) { 107 switch (which) { 108 case DialogInterface.BUTTON_POSITIVE: 109 switchToManagedProfile( 110 managedProfileUserId, 111 finalIntent); 112 finish(); 113 break; 114 case DialogInterface.BUTTON_NEGATIVE: 115 default: 116 finish(); 117 } 118 } 119 }; 120 121 AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this) 122 .setTitle(R.string.send_from_work_profile_title) 123 .setMessage(R.string.send_from_work_profile_description) 124 .setNegativeButton(R.string.send_from_work_profile_cancel, listener) 125 .setOnCancelListener( 126 new DialogInterface.OnCancelListener() { 127 @Override 128 public void onCancel(DialogInterface dialog) { 129 finish(); 130 } 131 }); 132 if (showPositiveActionButton) { 133 alertDialogBuilder.setPositiveButton(positiveButtonText, listener); 134 } 135 alertDialogBuilder.show(); 136 } 137 canStartActivityAsUser(Intent intent, int managedProfileUserId)138 private boolean canStartActivityAsUser(Intent intent, int managedProfileUserId) { 139 return !this.getPackageManager() 140 .queryIntentActivitiesAsUser( 141 intent, 142 PackageManager.ResolveInfoFlags.of(0), 143 managedProfileUserId) 144 .isEmpty(); 145 } 146 switchToManagedProfile(int managedProfileUserId, Intent intent)147 private void switchToManagedProfile(int managedProfileUserId, Intent intent) { 148 try { 149 startActivityAsUser(intent, 150 ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(), 151 UserHandle.of(managedProfileUserId)); 152 } catch (Exception e) { 153 Log.e(TAG, "Failed to switch to managed profile.", e); 154 } 155 } 156 getManagedProfileUserId(Context context, int userId)157 private static int getManagedProfileUserId(Context context, int userId) { 158 UserManager um = context.getSystemService(UserManager.class); 159 List<UserInfo> userProfiles = um.getProfiles(userId); 160 for (UserInfo uInfo : userProfiles) { 161 if (uInfo.id == userId) { 162 continue; 163 } 164 if (uInfo.isManagedProfile()) { 165 return uInfo.id; 166 } 167 } 168 return UserHandle.USER_NULL; 169 } 170 } 171