/* * Copyright (C) 2006-2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.policy.impl; import android.app.Activity; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IStatusBar; import android.content.BroadcastReceiver; import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.os.LocalPowerManager; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; import static android.provider.Settings.System.END_BUTTON_BEHAVIOR; import com.android.internal.policy.PolicyManager; import com.android.internal.telephony.ITelephony; import android.util.Config; import android.util.EventLog; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.OrientationListener; import android.view.RawInputEvent; import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManager; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; import android.view.WindowManagerPolicy.WindowState; import android.view.animation.Animation; import android.media.IAudioService; import android.media.AudioManager; import java.util.Observable; import java.util.Observer; /** * WindowManagerPolicy implementation for the Android MID UI. */ public class MidWindowManager implements WindowManagerPolicy { private static final String TAG = "MidWindowManager"; private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final boolean SHOW_STARTING_ANIMATIONS = true; private static final int APPLICATION_LAYER = 1; private static final int PHONE_LAYER = 2; private static final int SEARCH_BAR_LAYER = 3; private static final int STATUS_BAR_PANEL_LAYER = 4; // toasts and the plugged-in battery thing private static final int TOAST_LAYER = 5; private static final int STATUS_BAR_LAYER = 6; // SIM errors and unlock. Not sure if this really should be in a high layer. private static final int PRIORITY_PHONE_LAYER = 7; // like the ANR / app crashed dialogs private static final int SYSTEM_ALERT_LAYER = 8; // system-level error dialogs private static final int SYSTEM_ERROR_LAYER = 9; // the keyguard; nothing on top of these can take focus, since they are // responsible for power management when displayed. private static final int KEYGUARD_LAYER = 10; private static final int KEYGUARD_DIALOG_LAYER = 11; // things in here CAN NOT take focus, but are shown on top of everything else. private static final int SYSTEM_OVERLAY_LAYER = 12; static final int APPLICATION_MEDIA_SUBLAYER = -2; static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; static final int APPLICATION_PANEL_SUBLAYER = 1; static final int APPLICATION_SUB_PANEL_SUBLAYER = 2; private static final boolean SINGLE_PRESS_OFF = false; private static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f; static public final String SYSTEM_DIALOG_REASON_KEY = "reason"; static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; private Context mContext; private IWindowManager mWindowManager; private LocalPowerManager mPowerManager; boolean mSafeMode; private WindowState mStatusBar = null; private WindowState mSearchBar = null; private WindowState mKeyguard = null; private boolean mFirstConnect = true; private GlobalActions mGlobalActions; private boolean mShouldTurnOffOnKeyUp; private RecentApplicationsDialog mRecentAppsDialog; private Handler mHandler; private boolean mLidOpen; private boolean mScreenOn = false; private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; private int mW, mH; private int mCurLeft, mCurTop, mCurRight, mCurBottom; static final Rect mTmpParentFrame = new Rect(); static final Rect mTmpDisplayFrame = new Rect(); static final Rect mTmpVisibleFrame = new Rect(); private WindowState mTopFullscreenOpaqueWindowState; private boolean mForceStatusBar; private boolean mHomePressed; private Intent mHomeIntent; private boolean mSearchKeyPressed; private boolean mConsumeSearchKeyUp; private static final int ENDCALL_HOME = 0x1; private static final int ENDCALL_SLEEPS = 0x2; private static final int DEFAULT_ENDCALL_BEHAVIOR = ENDCALL_SLEEPS; private int mEndcallBehavior; private ShortcutManager mShortcutManager; private PowerManager.WakeLock mBroadcastWakeLock; /* * Various use cases for invoking this function * screen turning off, should always disable listeners if already enabled * screen turned on and current app has sensor based orientation, enable listeners * if not already enabled * screen turned on and current app does not have sensor orientation, disable listeners if * already enabled * screen turning on and current app has sensor based orientation, enable listeners if needed * screen turning on and current app has nosensor based orientation, do nothing */ private Runnable mEndCallLongPress = new Runnable() { public void run() { mShouldTurnOffOnKeyUp = false; sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); showGlobalActionsDialog(); } }; private void showGlobalActionsDialog() { if (mGlobalActions == null) { mGlobalActions = new GlobalActions(mContext, mPowerManager); } mGlobalActions.showDialog(false, isDeviceProvisioned()); } private boolean isDeviceProvisioned() { return Settings.System.getInt( mContext.getContentResolver(), Settings.System.DEVICE_PROVISIONED, 0) != 0; } /** * When a home-key longpress expires, close other system windows and launch the recent apps */ private Runnable mHomeLongPress = new Runnable() { public void run() { /* * Eat the longpress so it won't dismiss the recent apps dialog when * the user lets go of the home key */ mHomePressed = false; sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); showRecentAppsDialog(); } }; /** * Create (if necessary) and launch the recent apps dialog */ private void showRecentAppsDialog() { if (mRecentAppsDialog == null) { mRecentAppsDialog = new RecentApplicationsDialog(mContext); } mRecentAppsDialog.show(); } /** {@inheritDoc} */ public void init(Context context, IWindowManager windowManager, LocalPowerManager powerManager) { mContext = context; mWindowManager = windowManager; mPowerManager = powerManager; mHandler = new Handler(); mShortcutManager = new ShortcutManager(context, mHandler); mShortcutManager.observe(); mHomeIntent = new Intent(Intent.ACTION_MAIN, null); mHomeIntent.addCategory(Intent.CATEGORY_HOME); mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MidWindowManager.mBroadcastWakeLock"); } /** {@inheritDoc} */ public int checkAddPermission(WindowManager.LayoutParams attrs) { int type = attrs.type; if (type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW || type > WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) { return WindowManagerImpl.ADD_OKAY; } String permission = null; switch (type) { case TYPE_TOAST: // XXX right now the app process has complete control over // this... should introduce a token to let the system // monitor/control what they are doing. break; case TYPE_PHONE: case TYPE_PRIORITY_PHONE: case TYPE_SYSTEM_ALERT: case TYPE_SYSTEM_ERROR: case TYPE_SYSTEM_OVERLAY: permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW; break; default: permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; } if (permission != null) { if (mContext.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { return WindowManagerImpl.ADD_PERMISSION_DENIED; } } return WindowManagerImpl.ADD_OKAY; } public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_TOAST: // These types of windows can't receive input events. attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; } } /** {@inheritDoc} */ public void adjustConfigurationLw(Configuration config) { mPowerManager.setKeyboardVisibility(true); config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; mPowerManager.userActivity(SystemClock.uptimeMillis(), false, LocalPowerManager.OTHER_EVENT); } public boolean isCheekPressedAgainstScreen(MotionEvent ev) { return false; } /** {@inheritDoc} */ public int windowTypeToLayerLw(int type) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { return APPLICATION_LAYER; } switch (type) { case TYPE_APPLICATION_PANEL: return APPLICATION_LAYER; case TYPE_APPLICATION_SUB_PANEL: return APPLICATION_LAYER; case TYPE_STATUS_BAR: return STATUS_BAR_LAYER; case TYPE_STATUS_BAR_PANEL: return STATUS_BAR_PANEL_LAYER; case TYPE_SEARCH_BAR: return SEARCH_BAR_LAYER; case TYPE_PHONE: return PHONE_LAYER; case TYPE_KEYGUARD: return KEYGUARD_LAYER; case TYPE_KEYGUARD_DIALOG: return KEYGUARD_DIALOG_LAYER; case TYPE_SYSTEM_ALERT: return SYSTEM_ALERT_LAYER; case TYPE_SYSTEM_ERROR: return SYSTEM_ERROR_LAYER; case TYPE_SYSTEM_OVERLAY: return SYSTEM_OVERLAY_LAYER; case TYPE_PRIORITY_PHONE: return PRIORITY_PHONE_LAYER; case TYPE_TOAST: return TOAST_LAYER; } Log.e(TAG, "Unknown window type: " + type); return APPLICATION_LAYER; } /** {@inheritDoc} */ public int subWindowTypeToLayerLw(int type) { switch (type) { case TYPE_APPLICATION_PANEL: return APPLICATION_PANEL_SUBLAYER; case TYPE_APPLICATION_MEDIA: return APPLICATION_MEDIA_SUBLAYER; case TYPE_APPLICATION_MEDIA_OVERLAY: return APPLICATION_MEDIA_OVERLAY_SUBLAYER; case TYPE_APPLICATION_SUB_PANEL: return APPLICATION_SUB_PANEL_SUBLAYER; } Log.e(TAG, "Unknown sub-window type: " + type); return 0; } public int getMaxWallpaperLayer() { return STATUS_BAR_LAYER; } public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) { return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD; } public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) { return attrs.type != WindowManager.LayoutParams.TYPE_STATUS_BAR && attrs.type != WindowManager.LayoutParams.TYPE_WALLPAPER; } /** {@inheritDoc} */ public View addStartingWindow(IBinder appToken, String packageName, int theme, CharSequence nonLocalizedLabel, int labelRes, int icon) { if (!SHOW_STARTING_ANIMATIONS) { return null; } if (packageName == null) { return null; } Context context = mContext; boolean setTheme = false; //Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel=" // + nonLocalizedLabel + " theme=" + Integer.toHexString(theme)); if (theme != 0 || labelRes != 0) { try { context = context.createPackageContext(packageName, 0); if (theme != 0) { context.setTheme(theme); setTheme = true; } } catch (PackageManager.NameNotFoundException e) { } } if (!setTheme) { context.setTheme(com.android.internal.R.style.Theme); } Window win = PolicyManager.makeNewWindow(context); Resources r = context.getResources(); win.setTitle(r.getText(labelRes, nonLocalizedLabel)); win.setType( WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags( WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); win.setLayout(WindowManager.LayoutParams.FILL_PARENT, WindowManager.LayoutParams.FILL_PARENT); final WindowManager.LayoutParams params = win.getAttributes(); params.token = appToken; params.packageName = packageName; params.windowAnimations = win.getWindowStyle().getResourceId( com.android.internal.R.styleable.Window_windowAnimationStyle, 0); params.setTitle("Starting " + packageName); try { WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(Context.WINDOW_SERVICE); View view = win.getDecorView(); if (win.isFloating()) { // Whoops, there is no way to display an animation/preview // of such a thing! After all that work... let's skip it. // (Note that we must do this here because it is in // getDecorView() where the theme is evaluated... maybe // we should peek the floating attribute from the theme // earlier.) return null; } if (localLOGV) Log.v( TAG, "Adding starting window for " + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null)); wm.addView(view, params); // Only return the view if it was successfully added to the // window manager... which we can tell by it having a parent. return view.getParent() != null ? view : null; } catch (WindowManagerImpl.BadTokenException e) { // ignore Log.w(TAG, appToken + " already running, starting window not displayed"); } return null; } /** {@inheritDoc} */ public void removeStartingWindow(IBinder appToken, View window) { // RuntimeException e = new RuntimeException(); // Log.i(TAG, "remove " + appToken + " " + window, e); if (localLOGV) Log.v( TAG, "Removing starting window for " + appToken + ": " + window); if (window != null) { WindowManagerImpl wm = (WindowManagerImpl) mContext.getSystemService(Context.WINDOW_SERVICE); wm.removeView(window); } } /** * Preflight adding a window to the system. * * Currently enforces that three window types are singletons: *