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