blob: 22922e7bbac4496ab84de499c2d63ca4c97e2055 [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 Chung38d31c22017-11-08 14:32:32 -080037import com.android.systemui.shared.recents.IOverviewProxy;
38import com.android.systemui.shared.recents.ISystemUiProxy;
Winson Chung11f53e92017-11-13 17:45:12 -080039import com.android.systemui.shared.system.GraphicBufferCompat;
Matthew Ng7d05e772017-11-09 14:41:07 -080040import com.android.systemui.statusbar.policy.CallbackController;
Matthew Ng13dbf872017-10-27 11:02:14 -070041import com.android.systemui.statusbar.policy.DeviceProvisionedController;
42import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
Winson Chung11f53e92017-11-13 17:45:12 -080043
Matthew Ng1e43ebd2017-11-14 14:47:05 -080044import java.io.FileDescriptor;
45import java.io.PrintWriter;
Matthew Ng7d05e772017-11-09 14:41:07 -080046import java.util.ArrayList;
47import java.util.List;
Matthew Ng13dbf872017-10-27 11:02:14 -070048
49/**
50 * Class to send information from overview to launcher with a binder.
51 */
Matthew Ng1e43ebd2017-11-14 14:47:05 -080052public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
Matthew Ng13dbf872017-10-27 11:02:14 -070053
54 private static final String TAG = "OverviewProxyService";
55 private static final long BACKOFF_MILLIS = 5000;
56
57 private final Context mContext;
58 private final Handler mHandler;
Matthew Ngfac87832017-11-10 11:27:29 -080059 private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
Matthew Ng30c0a022017-11-10 14:06:29 -080060 private final ComponentName mLauncherComponentName;
Matthew Ng13dbf872017-10-27 11:02:14 -070061 private final DeviceProvisionedController mDeviceProvisionedController
62 = Dependency.get(DeviceProvisionedController.class);
Matthew Ng7d05e772017-11-09 14:41:07 -080063 private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
Matthew Ng13dbf872017-10-27 11:02:14 -070064
65 private IOverviewProxy mOverviewProxy;
66 private int mConnectionBackoffAttempts;
67
Winson Chung38d31c22017-11-08 14:32:32 -080068 private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
Winson Chung11f53e92017-11-13 17:45:12 -080069 public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
70 int maxLayer, boolean useIdentityTransform, int rotation) {
Winson Chung38d31c22017-11-08 14:32:32 -080071 long token = Binder.clearCallingIdentity();
72 try {
Winson Chung11f53e92017-11-13 17:45:12 -080073 return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
74 height, minLayer, maxLayer, useIdentityTransform, rotation));
Winson Chung38d31c22017-11-08 14:32:32 -080075 } finally {
76 Binder.restoreCallingIdentity(token);
77 }
78 }
79 };
80
Matthew Ng30c0a022017-11-10 14:06:29 -080081 private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
82 @Override
83 public void onReceive(Context context, Intent intent) {
84 // Reconnect immediately, instead of waiting for resume to arrive.
85 startConnectionToCurrentUser();
86 }
87 };
88
Matthew Ng13dbf872017-10-27 11:02:14 -070089 private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
90 @Override
91 public void onServiceConnected(ComponentName name, IBinder service) {
92 if (service != null) {
93 mConnectionBackoffAttempts = 0;
94 mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
95 // Listen for launcher's death
96 try {
97 service.linkToDeath(mOverviewServiceDeathRcpt, 0);
98 } catch (RemoteException e) {
99 Log.e(TAG, "Lost connection to launcher service", e);
100 }
Winson Chung38d31c22017-11-08 14:32:32 -0800101 try {
102 mOverviewProxy.onBind(mSysUiProxy);
103 } catch (RemoteException e) {
104 Log.e(TAG, "Failed to call onBind()", e);
105 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800106 notifyConnectionChanged();
Matthew Ng13dbf872017-10-27 11:02:14 -0700107 }
108 }
109
110 @Override
111 public void onServiceDisconnected(ComponentName name) {
112 // Do nothing
113 }
114 };
115
116 private final DeviceProvisionedListener mDeviceProvisionedCallback =
117 new DeviceProvisionedListener() {
118 @Override
Matthew Ngdfab86c2017-11-07 15:46:51 -0800119 public void onUserSetupChanged() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700120 if (mDeviceProvisionedController.isCurrentUserSetup()) {
Matthew Ngfac87832017-11-10 11:27:29 -0800121 internalConnectToCurrentUser();
Matthew Ng13dbf872017-10-27 11:02:14 -0700122 }
123 }
124
125 @Override
126 public void onUserSwitched() {
Matthew Ng13dbf872017-10-27 11:02:14 -0700127 mConnectionBackoffAttempts = 0;
Matthew Ngfac87832017-11-10 11:27:29 -0800128 internalConnectToCurrentUser();
Matthew Ng13dbf872017-10-27 11:02:14 -0700129 }
130 };
131
132 // This is the death handler for the binder from the launcher service
Matthew Ng30c0a022017-11-10 14:06:29 -0800133 private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
134 = this::startConnectionToCurrentUser;
Matthew Ng13dbf872017-10-27 11:02:14 -0700135
136 public OverviewProxyService(Context context) {
137 mContext = context;
138 mHandler = new Handler();
139 mConnectionBackoffAttempts = 0;
Matthew Ng30c0a022017-11-10 14:06:29 -0800140 mLauncherComponentName = ComponentName
141 .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
Matthew Ng13dbf872017-10-27 11:02:14 -0700142 mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
Matthew Ng30c0a022017-11-10 14:06:29 -0800143
144 // Listen for the package update changes.
145 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
146 filter.addDataScheme("package");
147 filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
148 PatternMatcher.PATTERN_LITERAL);
149 mContext.registerReceiver(mLauncherAddedReceiver, filter);
Matthew Ng13dbf872017-10-27 11:02:14 -0700150 }
151
152 public void startConnectionToCurrentUser() {
Matthew Ngfac87832017-11-10 11:27:29 -0800153 if (mHandler.getLooper() != Looper.myLooper()) {
154 mHandler.post(mConnectionRunnable);
155 } else {
156 internalConnectToCurrentUser();
157 }
158 }
159
160 private void internalConnectToCurrentUser() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800161 disconnectFromLauncherService();
162
Matthew Ng13dbf872017-10-27 11:02:14 -0700163 // If user has not setup yet or already connected, do not try to connect
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800164 if (!mDeviceProvisionedController.isCurrentUserSetup()) {
Matthew Ng13dbf872017-10-27 11:02:14 -0700165 return;
166 }
167 mHandler.removeCallbacks(mConnectionRunnable);
168 Intent launcherServiceIntent = new Intent();
Matthew Ng30c0a022017-11-10 14:06:29 -0800169 launcherServiceIntent.setComponent(mLauncherComponentName);
Matthew Ng13dbf872017-10-27 11:02:14 -0700170 boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
171 mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
172 UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
173 if (!bound) {
174 // Retry after exponential backoff timeout
175 final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
176 mHandler.postDelayed(mConnectionRunnable, timeoutMs);
177 mConnectionBackoffAttempts++;
178 }
179 }
180
Matthew Ng7d05e772017-11-09 14:41:07 -0800181 @Override
182 public void addCallback(OverviewProxyListener listener) {
183 mConnectionCallbacks.add(listener);
184 listener.onConnectionChanged(mOverviewProxy != null);
185 }
186
187 @Override
188 public void removeCallback(OverviewProxyListener listener) {
189 mConnectionCallbacks.remove(listener);
190 }
191
Matthew Ng13dbf872017-10-27 11:02:14 -0700192 public IOverviewProxy getProxy() {
193 return mOverviewProxy;
194 }
195
196 private void disconnectFromLauncherService() {
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800197 if (mOverviewProxy != null) {
Matthew Ngeb6893b2017-11-09 17:15:33 -0800198 mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800199 mContext.unbindService(mOverviewServiceConnection);
200 mOverviewProxy = null;
Matthew Ng7d05e772017-11-09 14:41:07 -0800201 notifyConnectionChanged();
Matthew Ng1fa3f7e2017-11-07 11:50:36 -0800202 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700203 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800204
205 private void notifyConnectionChanged() {
206 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
207 mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
208 }
209 }
210
Matthew Ng1e43ebd2017-11-14 14:47:05 -0800211 @Override
212 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
213 pw.println(TAG + " state:");
214 pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
215 pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
216 .isCurrentUserSetup());
217 pw.print(" isConnected="); pw.println(mOverviewProxy != null);
218 }
219
Matthew Ng7d05e772017-11-09 14:41:07 -0800220 public interface OverviewProxyListener {
221 void onConnectionChanged(boolean isConnected);
222 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700223}