1 /*
2  * Copyright (C) 2016 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 android.server.wm.app;
18 
19 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
20 import static android.server.wm.app.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
21 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
22 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_CUTOUT_EXISTS;
23 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
24 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
25 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
26 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
27 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
28 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
29 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
30 
31 import android.app.Activity;
32 import android.app.KeyguardManager;
33 import android.content.BroadcastReceiver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.os.Bundle;
38 import android.server.wm.ActivityLauncher;
39 import android.server.wm.TestJournalProvider;
40 import android.util.Log;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.view.Window;
44 import android.view.WindowInsets;
45 
46 import java.lang.ref.WeakReference;
47 
48 /**
49  * Activity that registers broadcast receiver .
50  */
51 public class BroadcastReceiverActivity extends Activity {
52     private static final String TAG = BroadcastReceiverActivity.class.getSimpleName();
53 
54     private TestBroadcastReceiver mBroadcastReceiver;
55 
56     @Override
onCreate(Bundle icicle)57     protected void onCreate(Bundle icicle) {
58         super.onCreate(icicle);
59 
60         final Object receiver = getLastNonConfigurationInstance();
61         if (receiver instanceof TestBroadcastReceiver) {
62             mBroadcastReceiver = (TestBroadcastReceiver) receiver;
63             mBroadcastReceiver.associate(this);
64         } else {
65             mBroadcastReceiver = new TestBroadcastReceiver(this);
66             mBroadcastReceiver.register();
67         }
68 
69         // Determine if a display cutout is present
70         final View view = new View(this);
71         getWindow().requestFeature(Window.FEATURE_NO_TITLE);
72         getWindow().getAttributes().layoutInDisplayCutoutMode =
73                 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
74         view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
75         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
76                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
77         setContentView(view);
78 
79         WindowInsets insets = getWindowManager().getCurrentWindowMetrics().getWindowInsets();
80         final boolean cutoutExists = (insets.getDisplayCutout() != null);
81         Log.i(TAG, "cutout=" + cutoutExists);
82         TestJournalProvider.putExtras(BroadcastReceiverActivity.this,
83                 bundle -> bundle.putBoolean(EXTRA_CUTOUT_EXISTS, cutoutExists));
84     }
85 
86     @Override
onDestroy()87     protected void onDestroy() {
88         super.onDestroy();
89         mBroadcastReceiver.destroy();
90     }
91 
92     @Override
onRetainNonConfigurationInstance()93     public Object onRetainNonConfigurationInstance() {
94         return mBroadcastReceiver;
95     }
96 
97     /**
98      * The receiver to perform action on the associated activity. If a broadcast intent is received
99      * while the activity is relaunching, it will be handled after the activity is recreated.
100      */
101     private static class TestBroadcastReceiver extends BroadcastReceiver {
102         final Context mAppContext;
103         WeakReference<Activity> mRef;
104 
TestBroadcastReceiver(Activity activity)105         TestBroadcastReceiver(Activity activity) {
106             mAppContext = activity.getApplicationContext();
107             associate(activity);
108         }
109 
register()110         void register() {
111             mAppContext.registerReceiver(this, new IntentFilter(ACTION_TRIGGER_BROADCAST));
112         }
113 
associate(Activity activity)114         void associate(Activity activity) {
115             mRef = new WeakReference<>(activity);
116         }
117 
destroy()118         void destroy() {
119             final Activity activity = mRef != null ? mRef.get() : null;
120             if (activity != null && activity.isChangingConfigurations()) {
121                 // The activity is destroyed for configuration change. Because it will be recreated
122                 // immediately the receiver only needs to associate to the new instance.
123                 return;
124             }
125             // The case of real finish.
126             mAppContext.unregisterReceiver(this);
127         }
128 
129         @Override
onReceive(Context context, Intent intent)130         public void onReceive(Context context, Intent intent) {
131             final Bundle extras = intent.getExtras();
132             if (extras == null) {
133                 return;
134             }
135             // Trigger unparcel so the log can show the content of extras.
136             extras.size();
137             Log.i(TAG, "onReceive: extras=" + extras);
138 
139             final Activity activity = mRef.get();
140             if (activity == null) {
141                 return;
142             }
143 
144             if (extras.getBoolean(EXTRA_FINISH_BROADCAST)) {
145                 activity.finish();
146             }
147             if (extras.getBoolean(EXTRA_MOVE_BROADCAST_TO_BACK)) {
148                 activity.moveTaskToBack(true);
149             }
150             if (extras.containsKey(EXTRA_BROADCAST_ORIENTATION)) {
151                 activity.setRequestedOrientation(extras.getInt(EXTRA_BROADCAST_ORIENTATION));
152             }
153             if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD)) {
154                 activity.getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
155             }
156             if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD_METHOD)) {
157                 activity.getSystemService(KeyguardManager.class).requestDismissKeyguard(activity,
158                         new KeyguardDismissLoggerCallback(context, BROADCAST_RECEIVER_ACTIVITY));
159             }
160 
161             ActivityLauncher.launchActivityFromExtras(activity, extras);
162         }
163     }
164 }
165