1 /* 2 * Copyright (C) 2013 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.app; 18 19 import static android.app.ActivityManager.START_CANCELED; 20 21 import android.content.Context; 22 import android.content.ContextWrapper; 23 import android.content.IIntentSender; 24 import android.content.Intent; 25 import android.content.IntentSender; 26 import android.graphics.SurfaceTexture; 27 import android.os.IBinder; 28 import android.os.Message; 29 import android.os.OperationCanceledException; 30 import android.os.RemoteException; 31 import android.util.AttributeSet; 32 import android.util.DisplayMetrics; 33 import android.util.Log; 34 import android.view.InputDevice; 35 import android.view.InputEvent; 36 import android.view.MotionEvent; 37 import android.view.Surface; 38 import android.view.TextureView; 39 import android.view.TextureView.SurfaceTextureListener; 40 import android.view.View; 41 import android.view.ViewGroup; 42 import android.view.WindowManager; 43 import dalvik.system.CloseGuard; 44 45 import java.lang.ref.WeakReference; 46 import java.util.ArrayDeque; 47 import java.util.concurrent.Executor; 48 import java.util.concurrent.BlockingQueue; 49 import java.util.concurrent.LinkedBlockingQueue; 50 import java.util.concurrent.ThreadFactory; 51 import java.util.concurrent.ThreadPoolExecutor; 52 import java.util.concurrent.TimeUnit; 53 import java.util.concurrent.atomic.AtomicInteger; 54 55 import com.android.internal.annotations.GuardedBy; 56 57 58 /** @hide */ 59 public class ActivityView extends ViewGroup { 60 private static final String TAG = "ActivityView"; 61 private static final boolean DEBUG = false; 62 63 private static final int MSG_SET_SURFACE = 1; 64 65 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 66 private static final int MINIMUM_POOL_SIZE = 1; 67 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 68 private static final int KEEP_ALIVE = 1; 69 70 private static final ThreadFactory sThreadFactory = new ThreadFactory() { 71 private final AtomicInteger mCount = new AtomicInteger(1); 72 73 public Thread newThread(Runnable r) { 74 return new Thread(r, "ActivityView #" + mCount.getAndIncrement()); 75 } 76 }; 77 78 private static final BlockingQueue<Runnable> sPoolWorkQueue = 79 new LinkedBlockingQueue<Runnable>(128); 80 81 /** 82 * An {@link Executor} that can be used to execute tasks in parallel. 83 */ 84 private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE, 85 MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 86 87 88 private static class SerialExecutor implements Executor { 89 private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 90 private Runnable mActive; 91 execute(final Runnable r)92 public synchronized void execute(final Runnable r) { 93 mTasks.offer(new Runnable() { 94 public void run() { 95 try { 96 r.run(); 97 } finally { 98 scheduleNext(); 99 } 100 } 101 }); 102 if (mActive == null) { 103 scheduleNext(); 104 } 105 } 106 scheduleNext()107 protected synchronized void scheduleNext() { 108 if ((mActive = mTasks.poll()) != null) { 109 sExecutor.execute(mActive); 110 } 111 } 112 } 113 114 private final SerialExecutor mExecutor = new SerialExecutor(); 115 116 private final int mDensityDpi; 117 private final TextureView mTextureView; 118 119 @GuardedBy("mActivityContainerLock") 120 private ActivityContainerWrapper mActivityContainer; 121 private Object mActivityContainerLock = new Object(); 122 123 private Activity mActivity; 124 private int mWidth; 125 private int mHeight; 126 private Surface mSurface; 127 private int mLastVisibility; 128 private ActivityViewCallback mActivityViewCallback; 129 130 ActivityView(Context context)131 public ActivityView(Context context) { 132 this(context, null); 133 } 134 ActivityView(Context context, AttributeSet attrs)135 public ActivityView(Context context, AttributeSet attrs) { 136 this(context, attrs, 0); 137 } 138 ActivityView(Context context, AttributeSet attrs, int defStyle)139 public ActivityView(Context context, AttributeSet attrs, int defStyle) { 140 super(context, attrs, defStyle); 141 142 while (context instanceof ContextWrapper) { 143 if (context instanceof Activity) { 144 mActivity = (Activity)context; 145 break; 146 } 147 context = ((ContextWrapper)context).getBaseContext(); 148 } 149 if (mActivity == null) { 150 throw new IllegalStateException("The ActivityView's Context is not an Activity."); 151 } 152 153 try { 154 mActivityContainer = new ActivityContainerWrapper( 155 ActivityManager.getService().createVirtualActivityContainer( 156 mActivity.getActivityToken(), new ActivityContainerCallback(this))); 157 } catch (RemoteException e) { 158 throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " 159 + e); 160 } 161 162 mTextureView = new TextureView(context); 163 mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener()); 164 addView(mTextureView); 165 166 WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE); 167 DisplayMetrics metrics = new DisplayMetrics(); 168 wm.getDefaultDisplay().getMetrics(metrics); 169 mDensityDpi = metrics.densityDpi; 170 171 mLastVisibility = getVisibility(); 172 173 if (DEBUG) Log.v(TAG, "ctor()"); 174 } 175 176 @Override onLayout(boolean changed, int l, int t, int r, int b)177 protected void onLayout(boolean changed, int l, int t, int r, int b) { 178 mTextureView.layout(0, 0, r - l, b - t); 179 } 180 181 @Override onVisibilityChanged(View changedView, final int visibility)182 protected void onVisibilityChanged(View changedView, final int visibility) { 183 super.onVisibilityChanged(changedView, visibility); 184 185 if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) { 186 if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable"); 187 final Surface surface = (visibility == View.GONE) ? null : mSurface; 188 setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false); 189 } 190 mLastVisibility = visibility; 191 } 192 injectInputEvent(InputEvent event)193 private boolean injectInputEvent(InputEvent event) { 194 return mActivityContainer != null && mActivityContainer.injectEvent(event); 195 } 196 197 @Override onTouchEvent(MotionEvent event)198 public boolean onTouchEvent(MotionEvent event) { 199 return injectInputEvent(event) || super.onTouchEvent(event); 200 } 201 202 @Override onGenericMotionEvent(MotionEvent event)203 public boolean onGenericMotionEvent(MotionEvent event) { 204 if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { 205 if (injectInputEvent(event)) { 206 return true; 207 } 208 } 209 return super.onGenericMotionEvent(event); 210 } 211 212 @Override onAttachedToWindow()213 public void onAttachedToWindow() { 214 if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer + 215 " mSurface=" + mSurface); 216 } 217 218 @Override onDetachedFromWindow()219 public void onDetachedFromWindow() { 220 if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer + 221 " mSurface=" + mSurface); 222 } 223 isAttachedToDisplay()224 public boolean isAttachedToDisplay() { 225 return mSurface != null; 226 } 227 startActivity(Intent intent)228 public void startActivity(Intent intent) { 229 if (mActivityContainer == null) { 230 throw new IllegalStateException("Attempt to call startActivity after release"); 231 } 232 if (mSurface == null) { 233 throw new IllegalStateException("Surface not yet created."); 234 } 235 if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " + 236 (isAttachedToDisplay() ? "" : "not") + " attached"); 237 if (mActivityContainer.startActivity(intent) == START_CANCELED) { 238 throw new OperationCanceledException(); 239 } 240 } 241 startActivity(IntentSender intentSender)242 public void startActivity(IntentSender intentSender) { 243 if (mActivityContainer == null) { 244 throw new IllegalStateException("Attempt to call startActivity after release"); 245 } 246 if (mSurface == null) { 247 throw new IllegalStateException("Surface not yet created."); 248 } 249 if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " + 250 (isAttachedToDisplay() ? "" : "not") + " attached"); 251 final IIntentSender iIntentSender = intentSender.getTarget(); 252 if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) { 253 throw new OperationCanceledException(); 254 } 255 } 256 startActivity(PendingIntent pendingIntent)257 public void startActivity(PendingIntent pendingIntent) { 258 if (mActivityContainer == null) { 259 throw new IllegalStateException("Attempt to call startActivity after release"); 260 } 261 if (mSurface == null) { 262 throw new IllegalStateException("Surface not yet created."); 263 } 264 if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " " 265 + (isAttachedToDisplay() ? "" : "not") + " attached"); 266 final IIntentSender iIntentSender = pendingIntent.getTarget(); 267 if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) { 268 throw new OperationCanceledException(); 269 } 270 } 271 release()272 public void release() { 273 if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer + 274 " mSurface=" + mSurface); 275 if (mActivityContainer == null) { 276 Log.e(TAG, "Duplicate call to release"); 277 return; 278 } 279 synchronized (mActivityContainerLock) { 280 mActivityContainer.release(); 281 mActivityContainer = null; 282 } 283 284 if (mSurface != null) { 285 mSurface.release(); 286 mSurface = null; 287 } 288 289 mTextureView.setSurfaceTextureListener(null); 290 } 291 setSurfaceAsync(final Surface surface, final int width, final int height, final int densityDpi, final boolean callback)292 private void setSurfaceAsync(final Surface surface, final int width, final int height, 293 final int densityDpi, final boolean callback) { 294 mExecutor.execute(new Runnable() { 295 public void run() { 296 try { 297 synchronized (mActivityContainerLock) { 298 if (mActivityContainer != null) { 299 mActivityContainer.setSurface(surface, width, height, densityDpi); 300 } 301 } 302 } catch (RemoteException e) { 303 throw new RuntimeException( 304 "ActivityView: Unable to set surface of ActivityContainer. ", 305 e); 306 } 307 if (callback) { 308 post(new Runnable() { 309 @Override 310 public void run() { 311 if (mActivityViewCallback != null) { 312 if (surface != null) { 313 mActivityViewCallback.onSurfaceAvailable(ActivityView.this); 314 } else { 315 mActivityViewCallback.onSurfaceDestroyed(ActivityView.this); 316 } 317 } 318 } 319 }); 320 } 321 } 322 }); 323 } 324 325 /** 326 * Set the callback to use to report certain state changes. 327 * 328 * Note: If the surface has been created prior to this call being made, then 329 * ActivityViewCallback.onSurfaceAvailable will be called from within setCallback. 330 * 331 * @param callback The callback to report events to. 332 * 333 * @see ActivityViewCallback 334 */ setCallback(ActivityViewCallback callback)335 public void setCallback(ActivityViewCallback callback) { 336 mActivityViewCallback = callback; 337 338 if (mSurface != null) { 339 mActivityViewCallback.onSurfaceAvailable(this); 340 } 341 } 342 343 public static abstract class ActivityViewCallback { 344 /** 345 * Called when all activities in the ActivityView have completed and been removed. Register 346 * using {@link ActivityView#setCallback(ActivityViewCallback)}. Each ActivityView may 347 * have at most one callback registered. 348 */ onAllActivitiesComplete(ActivityView view)349 public abstract void onAllActivitiesComplete(ActivityView view); 350 /** 351 * Called when the surface is ready to be drawn to. Calling startActivity prior to this 352 * callback will result in an IllegalStateException. 353 */ onSurfaceAvailable(ActivityView view)354 public abstract void onSurfaceAvailable(ActivityView view); 355 /** 356 * Called when the surface has been removed. Calling startActivity after this callback 357 * will result in an IllegalStateException. 358 */ onSurfaceDestroyed(ActivityView view)359 public abstract void onSurfaceDestroyed(ActivityView view); 360 } 361 362 private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener { 363 @Override onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height)364 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, 365 int height) { 366 if (mActivityContainer == null) { 367 return; 368 } 369 if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height=" 370 + height); 371 mWidth = width; 372 mHeight = height; 373 mSurface = new Surface(surfaceTexture); 374 setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true); 375 } 376 377 @Override onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height)378 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, 379 int height) { 380 if (mActivityContainer == null) { 381 return; 382 } 383 if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height); 384 } 385 386 @Override onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture)387 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { 388 if (mActivityContainer == null) { 389 return true; 390 } 391 if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed"); 392 mSurface.release(); 393 mSurface = null; 394 setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true); 395 return true; 396 } 397 398 @Override onSurfaceTextureUpdated(SurfaceTexture surfaceTexture)399 public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { 400 // Log.d(TAG, "onSurfaceTextureUpdated"); 401 } 402 403 } 404 405 private static class ActivityContainerCallback extends IActivityContainerCallback.Stub { 406 private final WeakReference<ActivityView> mActivityViewWeakReference; 407 ActivityContainerCallback(ActivityView activityView)408 ActivityContainerCallback(ActivityView activityView) { 409 mActivityViewWeakReference = new WeakReference<>(activityView); 410 } 411 412 @Override setVisible(IBinder container, boolean visible)413 public void setVisible(IBinder container, boolean visible) { 414 if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible + 415 " ActivityView=" + mActivityViewWeakReference.get()); 416 } 417 418 @Override onAllActivitiesComplete(IBinder container)419 public void onAllActivitiesComplete(IBinder container) { 420 final ActivityView activityView = mActivityViewWeakReference.get(); 421 if (activityView != null) { 422 final ActivityViewCallback callback = activityView.mActivityViewCallback; 423 if (callback != null) { 424 final WeakReference<ActivityViewCallback> callbackRef = 425 new WeakReference<>(callback); 426 activityView.post(new Runnable() { 427 @Override 428 public void run() { 429 ActivityViewCallback callback = callbackRef.get(); 430 if (callback != null) { 431 callback.onAllActivitiesComplete(activityView); 432 } 433 } 434 }); 435 } 436 } 437 } 438 } 439 440 private static class ActivityContainerWrapper { 441 private final IActivityContainer mIActivityContainer; 442 private final CloseGuard mGuard = CloseGuard.get(); 443 boolean mOpened; // Protected by mGuard. 444 ActivityContainerWrapper(IActivityContainer container)445 ActivityContainerWrapper(IActivityContainer container) { 446 mIActivityContainer = container; 447 mOpened = true; 448 mGuard.open("release"); 449 } 450 setSurface(Surface surface, int width, int height, int density)451 void setSurface(Surface surface, int width, int height, int density) 452 throws RemoteException { 453 mIActivityContainer.setSurface(surface, width, height, density); 454 } 455 startActivity(Intent intent)456 int startActivity(Intent intent) { 457 try { 458 return mIActivityContainer.startActivity(intent); 459 } catch (RemoteException e) { 460 throw new RuntimeException("ActivityView: Unable to startActivity. " + e); 461 } 462 } 463 startActivityIntentSender(IIntentSender intentSender)464 int startActivityIntentSender(IIntentSender intentSender) { 465 try { 466 return mIActivityContainer.startActivityIntentSender(intentSender); 467 } catch (RemoteException e) { 468 throw new RuntimeException( 469 "ActivityView: Unable to startActivity from IntentSender. " + e); 470 } 471 } 472 getDisplayId()473 int getDisplayId() { 474 try { 475 return mIActivityContainer.getDisplayId(); 476 } catch (RemoteException e) { 477 return -1; 478 } 479 } 480 injectEvent(InputEvent event)481 boolean injectEvent(InputEvent event) { 482 try { 483 return mIActivityContainer.injectEvent(event); 484 } catch (RemoteException e) { 485 return false; 486 } 487 } 488 release()489 void release() { 490 synchronized (mGuard) { 491 if (mOpened) { 492 if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); 493 try { 494 mIActivityContainer.release(); 495 mGuard.close(); 496 } catch (RemoteException e) { 497 } 498 mOpened = false; 499 } 500 } 501 } 502 503 @Override finalize()504 protected void finalize() throws Throwable { 505 if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called"); 506 try { 507 if (mGuard != null) { 508 mGuard.warnIfOpen(); 509 release(); 510 } 511 } finally { 512 super.finalize(); 513 } 514 } 515 516 } 517 } 518