blob: 8cff56df1730330283bc175665a18b2ee8a7cdd1 [file] [log] [blame]
Matthew Ng13dbf872017-10-27 11:02:14 -07001/*
2 * Copyright (C) 2017 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
17package com.android.systemui;
18
Matthew Ng30c0a022017-11-10 14:06:29 -080019import android.content.BroadcastReceiver;
Matthew Ng13dbf872017-10-27 11:02:14 -070020import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
Matthew Ng30c0a022017-11-10 14:06:29 -080023import android.content.IntentFilter;
Matthew Ng13dbf872017-10-27 11:02:14 -070024import android.content.ServiceConnection;
Winson Chung38d31c22017-11-08 14:32:32 -080025import android.graphics.Rect;
26import android.os.Binder;
Matthew Ng13dbf872017-10-27 11:02:14 -070027import android.os.Handler;
28import android.os.IBinder;
Matthew Ngfac87832017-11-10 11:27:29 -080029import android.os.Looper;
Matthew Ng30c0a022017-11-10 14:06:29 -080030import android.os.PatternMatcher;
Matthew Ng13dbf872017-10-27 11:02:14 -070031import android.os.RemoteException;
32import android.os.UserHandle;
33import android.util.Log;
Winson Chung38d31c22017-11-08 14:32:32 -080034import android.view.SurfaceControl;
35
Winson Chung11f53e92017-11-13 17:45:12 -080036import com.android.systemui.OverviewProxyService.OverviewProxyListener;
Winson Chungc1674272018-02-21 10:15:17 -080037import com.android.systemui.recents.events.EventBus;
38import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
Matthew Ng1b1d3462018-03-02 11:43:38 -080039import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung38d31c22017-11-08 14:32:32 -080040import com.android.systemui.shared.recents.IOverviewProxy;
41import com.android.systemui.shared.recents.ISystemUiProxy;
Winson Chung11f53e92017-11-13 17:45:12 -080042import com.android.systemui.shared.system.GraphicBufferCompat;
Winson Chungcaf2b812018-01-26 10:29:46 -080043import com.android.systemui.statusbar.phone.StatusBar;
Matthew Ng7d05e772017-11-09 14:41:07 -080044import com.android.systemui.statusbar.policy.CallbackController;
Matthew Ng13dbf872017-10-27 11:02:14 -070045import com.android.systemui.statusbar.policy.DeviceProvisionedController;
46import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
Winson Chung11f53e92017-11-13 17:45:12 -080047
Matthew Ng1e43ebd2017-11-14 14:47:05 -080048import java.io.FileDescriptor;
49import java.io.PrintWriter;
Matthew Ng7d05e772017-11-09 14:41:07 -080050import java.util.ArrayList;
51import java.util.List;
Matthew Ng13dbf872017-10-27 11:02:14 -070052
Matthew Ngc1a97b12018-03-28 14:02:00 -070053import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
Matthew Ng8f25fb962018-01-16 17:17:24 -080054import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
55
Matthew Ng13dbf872017-10-27 11:02:14 -070056/**
57 * Class to send information from overview to launcher with a binder.
58 */
Matthew Ng1e43ebd2017-11-14 14:47:05 -080059public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
Matthew Ng13dbf872017-10-27 11:02:14 -070060
Sunny Goyal09c12d72018-03-27 10:14:49 -070061 private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
62
Matthew Ngbd824572018-01-17 16:25:56 -080063 public static final String TAG_OPS = "OverviewProxyService";
64 public static final boolean DEBUG_OVERVIEW_PROXY = false;
Matthew Ng13dbf872017-10-27 11:02:14 -070065 private static final long BACKOFF_MILLIS = 5000;
66
67 private final Context mContext;
68 private final Handler mHandler;
Matthew Ngfac87832017-11-10 11:27:29 -080069 private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
Sunny Goyal09c12d72018-03-27 10:14:49 -070070 private final ComponentName mRecentsComponentName;
Matthew Ng13dbf872017-10-27 11:02:14 -070071 private final DeviceProvisionedController mDeviceProvisionedController
72 = Dependency.get(DeviceProvisionedController.class);
Matthew Ng7d05e772017-11-09 14:41:07 -080073 private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
Matthew Ng13dbf872017-10-27 11:02:14 -070074
75 private IOverviewProxy mOverviewProxy;
76 private int mConnectionBackoffAttempts;
Tony Wickham05c1f852018-02-06 12:32:54 -080077 private CharSequence mOnboardingText;
Matthew Ng8f25fb962018-01-16 17:17:24 -080078 private @InteractionType int mInteractionFlags;
Matthew Ng13dbf872017-10-27 11:02:14 -070079
Winson Chung38d31c22017-11-08 14:32:32 -080080 private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
Winson Chungcaf2b812018-01-26 10:29:46 -080081
Winson Chung11f53e92017-11-13 17:45:12 -080082 public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
83 int maxLayer, boolean useIdentityTransform, int rotation) {
Winson Chung38d31c22017-11-08 14:32:32 -080084 long token = Binder.clearCallingIdentity();
85 try {
Winson Chung11f53e92017-11-13 17:45:12 -080086 return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
87 height, minLayer, maxLayer, useIdentityTransform, rotation));
Winson Chung38d31c22017-11-08 14:32:32 -080088 } finally {
89 Binder.restoreCallingIdentity(token);
90 }
91 }
Winson Chungcbb15a92018-01-25 17:46:16 +000092
Winson Chungcaf2b812018-01-26 10:29:46 -080093 public void startScreenPinning(int taskId) {
94 long token = Binder.clearCallingIdentity();
95 try {
96 mHandler.post(() -> {
97 StatusBar statusBar = ((SystemUIApplication) mContext).getComponent(
98 StatusBar.class);
99 if (statusBar != null) {
100 statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
101 }
102 });
103 } finally {
104 Binder.restoreCallingIdentity(token);
105 }
106 }
107
Winson Chungc1674272018-02-21 10:15:17 -0800108 public void onSplitScreenInvoked() {
109 long token = Binder.clearCallingIdentity();
110 try {
111 EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
112 } finally {
113 Binder.restoreCallingIdentity(token);
114 }
115 }
116
Tony Wickham05c1f852018-02-06 12:32:54 -0800117 public void setRecentsOnboardingText(CharSequence text) {
118 mOnboardingText = text;
119 }
Matthew Ng8f25fb962018-01-16 17:17:24 -0800120
121 public void setInteractionState(@InteractionType int flags) {
122 long token = Binder.clearCallingIdentity();
123 try {
124 if (mInteractionFlags != flags) {
125 mInteractionFlags = flags;
126 mHandler.post(() -> {
127 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
128 mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
129 }
130 });
131 }
132 } finally {
133 Binder.restoreCallingIdentity(token);
134 }
135 }
Winson Chung38d31c22017-11-08 14:32:32 -0800136 };
137
Winson Chung317ee502018-04-04 15:16:00 +0000138 private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
Matthew Ng30c0a022017-11-10 14:06:29 -0800139 @Override
140 public void onReceive(Context context, Intent intent) {
141 // Reconnect immediately, instead of waiting for resume to arrive.
142 startConnectionToCurrentUser();
143 }
144 };
145
Matthew Ng13dbf872017-10-27 11:02:14 -0700146 private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
147 @Override
148 public void onServiceConnected(ComponentName name, IBinder service) {
149 if (service != null) {
150 mConnectionBackoffAttempts = 0;
151 mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
152 // Listen for launcher's death
153 try {
154 service.linkToDeath(mOverviewServiceDeathRcpt, 0);
155 } catch (RemoteException e) {
Matthew Ngbd824572018-01-17 16:25:56 -0800156 Log.e(TAG_OPS, "Lost connection to launcher service", e);
Matthew Ng13dbf872017-10-27 11:02:14 -0700157 }
Winson Chung38d31c22017-11-08 14:32:32 -0800158 try {
159 mOverviewProxy.onBind(mSysUiProxy);
160 } catch (RemoteException e) {
Matthew Ngbd824572018-01-17 16:25:56 -0800161 Log.e(TAG_OPS, "Failed to call onBind()", e);
Winson Chung38d31c22017-11-08 14:32:32 -0800162 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800163 notifyConnectionChanged();
Matthew Ng13dbf872017-10-27 11:02:14 -0700164 }
165 }
166
167 @Override
168 public void onServiceDisconnected(ComponentName name) {
169 // Do nothing
170 }
171 };
172
173 private final DeviceProvisionedListener mDeviceProvisionedCallback =
174 new DeviceProvisionedListener() {
175 @Override
Matthew Ngdfab86c2017-11-07 15:46:51 -0800176 public void onUserSetupChanged() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700177 if (mDeviceProvisionedController.isCurrentUserSetup()) {
Matthew Ngfac87832017-11-10 11:27:29 -0800178 internalConnectToCurrentUser();
Matthew Ng13dbf872017-10-27 11:02:14 -0700179 }
180 }
181
182 @Override
183 public void onUserSwitched() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700184 mConnectionBackoffAttempts = 0;
Matthew Ngfac87832017-11-10 11:27:29 -0800185 internalConnectToCurrentUser();
Matthew Ng13dbf872017-10-27 11:02:14 -0700186 }
187 };
188
189 // This is the death handler for the binder from the launcher service
Matthew Ng30c0a022017-11-10 14:06:29 -0800190 private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
191 = this::startConnectionToCurrentUser;
Matthew Ng13dbf872017-10-27 11:02:14 -0700192
193 public OverviewProxyService(Context context) {
194 mContext = context;
195 mHandler = new Handler();
196 mConnectionBackoffAttempts = 0;
Sunny Goyal09c12d72018-03-27 10:14:49 -0700197 mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
198 com.android.internal.R.string.config_recentsComponentName));
Matthew Ng30c0a022017-11-10 14:06:29 -0800199
200 // Listen for the package update changes.
Matthew Ng1b1d3462018-03-02 11:43:38 -0800201 if (SystemServicesProxy.getInstance(context)
202 .isSystemUser(mDeviceProvisionedController.getCurrentUser())) {
203 mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
204 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
205 filter.addDataScheme("package");
Sunny Goyal09c12d72018-03-27 10:14:49 -0700206 filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
Matthew Ng1b1d3462018-03-02 11:43:38 -0800207 PatternMatcher.PATTERN_LITERAL);
208 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Winson Chung317ee502018-04-04 15:16:00 +0000209 mContext.registerReceiver(mLauncherAddedReceiver, filter);
Matthew Ng1b1d3462018-03-02 11:43:38 -0800210 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700211 }
212
213 public void startConnectionToCurrentUser() {
Matthew Ngfac87832017-11-10 11:27:29 -0800214 if (mHandler.getLooper() != Looper.myLooper()) {
215 mHandler.post(mConnectionRunnable);
216 } else {
217 internalConnectToCurrentUser();
218 }
219 }
220
221 private void internalConnectToCurrentUser() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800222 disconnectFromLauncherService();
223
Matthew Ng13dbf872017-10-27 11:02:14 -0700224 // If user has not setup yet or already connected, do not try to connect
Winson Chung317ee502018-04-04 15:16:00 +0000225 if (!mDeviceProvisionedController.isCurrentUserSetup()) {
Matthew Ng13dbf872017-10-27 11:02:14 -0700226 return;
227 }
228 mHandler.removeCallbacks(mConnectionRunnable);
Sunny Goyal09c12d72018-03-27 10:14:49 -0700229 Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
230 .setPackage(mRecentsComponentName.getPackageName());
Matthew Ng10b6c41a2018-03-26 18:01:37 -0700231 boolean bound = false;
232 try {
233 bound = mContext.bindServiceAsUser(launcherServiceIntent,
234 mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
235 UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
236 } catch (SecurityException e) {
237 Log.e(TAG_OPS, "Unable to bind because of security error", e);
238 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700239 if (!bound) {
240 // Retry after exponential backoff timeout
241 final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
242 mHandler.postDelayed(mConnectionRunnable, timeoutMs);
243 mConnectionBackoffAttempts++;
244 }
245 }
246
Matthew Ng7d05e772017-11-09 14:41:07 -0800247 @Override
248 public void addCallback(OverviewProxyListener listener) {
249 mConnectionCallbacks.add(listener);
250 listener.onConnectionChanged(mOverviewProxy != null);
251 }
252
253 @Override
254 public void removeCallback(OverviewProxyListener listener) {
255 mConnectionCallbacks.remove(listener);
256 }
257
Matthew Ngc1a97b12018-03-28 14:02:00 -0700258 public boolean shouldShowSwipeUpUI() {
Winson Chung317ee502018-04-04 15:16:00 +0000259 return getProxy() != null && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
Matthew Ngc1a97b12018-03-28 14:02:00 -0700260 }
261
Matthew Ng13dbf872017-10-27 11:02:14 -0700262 public IOverviewProxy getProxy() {
263 return mOverviewProxy;
264 }
265
Tony Wickham05c1f852018-02-06 12:32:54 -0800266 public CharSequence getOnboardingText() {
267 return mOnboardingText;
Tony Wickhamfb63fe82018-01-16 12:14:06 -0800268 }
269
Matthew Ng8f25fb962018-01-16 17:17:24 -0800270 public int getInteractionFlags() {
271 return mInteractionFlags;
272 }
273
Matthew Ng13dbf872017-10-27 11:02:14 -0700274 private void disconnectFromLauncherService() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800275 if (mOverviewProxy != null) {
Matthew Ngeb6893b2017-11-09 17:15:33 -0800276 mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800277 mContext.unbindService(mOverviewServiceConnection);
278 mOverviewProxy = null;
Matthew Ng7d05e772017-11-09 14:41:07 -0800279 notifyConnectionChanged();
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800280 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700281 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800282
283 private void notifyConnectionChanged() {
284 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
285 mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
286 }
287 }
288
Matthew Ng2ea93b72018-03-14 19:43:18 +0000289 public void notifyQuickStepStarted() {
Winson Chungcbb15a92018-01-25 17:46:16 +0000290 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000291 mConnectionCallbacks.get(i).onQuickStepStarted();
Winson Chungcbb15a92018-01-25 17:46:16 +0000292 }
293 }
294
Matthew Ng1e43ebd2017-11-14 14:47:05 -0800295 @Override
296 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Matthew Ngbd824572018-01-17 16:25:56 -0800297 pw.println(TAG_OPS + " state:");
Matthew Ng1e43ebd2017-11-14 14:47:05 -0800298 pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
299 pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
300 .isCurrentUserSetup());
301 pw.print(" isConnected="); pw.println(mOverviewProxy != null);
302 }
303
Matthew Ng7d05e772017-11-09 14:41:07 -0800304 public interface OverviewProxyListener {
Winson Chungcbb15a92018-01-25 17:46:16 +0000305 default void onConnectionChanged(boolean isConnected) {}
Matthew Ng2ea93b72018-03-14 19:43:18 +0000306 default void onQuickStepStarted() {}
Matthew Ng8f25fb962018-01-16 17:17:24 -0800307 default void onInteractionFlagsChanged(@InteractionType int flags) {}
Matthew Ng7d05e772017-11-09 14:41:07 -0800308 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700309}