1/*
2 * Copyright (C) 2006-2007 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.internal.policy.impl;
18
19import android.app.Activity;
20import android.app.ActivityManagerNative;
21import android.app.IActivityManager;
22import android.app.IStatusBar;
23import android.content.BroadcastReceiver;
24import android.content.ContentQueryMap;
25import android.content.ContentResolver;
26import android.content.ContentValues;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.ActivityInfo;
30import android.content.pm.PackageManager;
31import android.content.res.Configuration;
32import android.content.res.Resources;
33import android.database.Cursor;
34import android.graphics.Rect;
35import android.os.Handler;
36import android.os.IBinder;
37import android.os.LocalPowerManager;
38import android.os.PowerManager;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.os.SystemClock;
42import android.provider.Settings;
43import static android.provider.Settings.System.END_BUTTON_BEHAVIOR;
44
45import com.android.internal.policy.PolicyManager;
46import com.android.internal.telephony.ITelephony;
47import android.util.Config;
48import android.util.EventLog;
49import android.util.Log;
50import android.view.HapticFeedbackConstants;
51import android.view.IWindowManager;
52import android.view.KeyEvent;
53import android.view.MotionEvent;
54import android.view.OrientationListener;
55import android.view.RawInputEvent;
56import android.view.Surface;
57import android.view.View;
58import android.view.ViewConfiguration;
59import android.view.Window;
60import android.view.WindowManager;
61import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
62import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
63import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
64import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
65import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
66import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
67import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
68import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
69import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
70import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
71import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
72import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
73import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
74import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
75import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
76import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
77import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
78import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
79import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
80import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
81import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
82import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
83import android.view.WindowManagerImpl;
84import android.view.WindowManagerPolicy;
85import android.view.WindowManagerPolicy.WindowState;
86import android.media.IAudioService;
87import android.media.AudioManager;
88
89import java.util.Observable;
90import java.util.Observer;
91
92/**
93 * WindowManagerPolicy implementation for the Android MID UI.
94 */
95public class MidWindowManager implements WindowManagerPolicy {
96    private static final String TAG = "MidWindowManager";
97    private static final boolean DEBUG = false;
98    private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
99    private static final boolean SHOW_STARTING_ANIMATIONS = true;
100
101    private static final int APPLICATION_LAYER = 1;
102    private static final int PHONE_LAYER = 2;
103    private static final int SEARCH_BAR_LAYER = 3;
104    private static final int STATUS_BAR_PANEL_LAYER = 4;
105    // toasts and the plugged-in battery thing
106    private static final int TOAST_LAYER = 5;
107    private static final int STATUS_BAR_LAYER = 6;
108    // SIM errors and unlock.  Not sure if this really should be in a high layer.
109    private static final int PRIORITY_PHONE_LAYER = 7;
110    // like the ANR / app crashed dialogs
111    private static final int SYSTEM_ALERT_LAYER = 8;
112    // system-level error dialogs
113    private static final int SYSTEM_ERROR_LAYER = 9;
114    // the keyguard; nothing on top of these can take focus, since they are
115    // responsible for power management when displayed.
116    private static final int KEYGUARD_LAYER = 10;
117    private static final int KEYGUARD_DIALOG_LAYER = 11;
118    // things in here CAN NOT take focus, but are shown on top of everything else.
119    private static final int SYSTEM_OVERLAY_LAYER = 12;
120
121    static final int APPLICATION_MEDIA_SUBLAYER = -2;
122    static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
123    static final int APPLICATION_PANEL_SUBLAYER = 1;
124    static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
125
126    private static final boolean SINGLE_PRESS_OFF = false;
127
128    private static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f;
129
130    static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
131    static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
132    static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
133
134    private Context mContext;
135    private IWindowManager mWindowManager;
136    private LocalPowerManager mPowerManager;
137
138    boolean mSafeMode;
139    private WindowState mStatusBar = null;
140    private WindowState mSearchBar = null;
141    private WindowState mKeyguard = null;
142    private boolean mFirstConnect = true;
143    private GlobalActions mGlobalActions;
144    private boolean mShouldTurnOffOnKeyUp;
145    private RecentApplicationsDialog mRecentAppsDialog;
146    private Handler mHandler;
147
148    private boolean mLidOpen;
149    private boolean mScreenOn = false;
150    private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
151
152    private int mW, mH;
153    private int mCurLeft, mCurTop, mCurRight, mCurBottom;
154
155    static final Rect mTmpParentFrame = new Rect();
156    static final Rect mTmpDisplayFrame = new Rect();
157    static final Rect mTmpVisibleFrame = new Rect();
158
159    private WindowState mTopFullscreenOpaqueWindowState;
160    private boolean mForceStatusBar;
161    private boolean mHomePressed;
162    private Intent mHomeIntent;
163    private boolean mSearchKeyPressed;
164    private boolean mConsumeSearchKeyUp;
165
166    private static final int ENDCALL_HOME = 0x1;
167    private static final int ENDCALL_SLEEPS = 0x2;
168    private static final int DEFAULT_ENDCALL_BEHAVIOR = ENDCALL_SLEEPS;
169    private int mEndcallBehavior;
170
171    private ShortcutManager mShortcutManager;
172    private PowerManager.WakeLock mBroadcastWakeLock;
173
174    /*
175     * Various use cases for invoking this function
176     * screen turning off, should always disable listeners if already enabled
177     * screen turned on and current app has sensor based orientation, enable listeners
178     * if not already enabled
179     * screen turned on and current app does not have sensor orientation, disable listeners if
180     * already enabled
181     * screen turning on and current app has sensor based orientation, enable listeners if needed
182     * screen turning on and current app has nosensor based orientation, do nothing
183     */
184
185    private Runnable mEndCallLongPress = new Runnable() {
186        public void run() {
187            mShouldTurnOffOnKeyUp = false;
188            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
189            showGlobalActionsDialog();
190        }
191    };
192
193    private void showGlobalActionsDialog() {
194        if (mGlobalActions == null) {
195            mGlobalActions = new GlobalActions(mContext, mPowerManager);
196        }
197        mGlobalActions.showDialog(false, isDeviceProvisioned());
198    }
199
200    private boolean isDeviceProvisioned() {
201        return Settings.System.getInt(
202                mContext.getContentResolver(), Settings.System.DEVICE_PROVISIONED, 0) != 0;
203    }
204
205    /**
206     * When a home-key longpress expires, close other system windows and launch the recent apps
207     */
208    private Runnable mHomeLongPress = new Runnable() {
209        public void run() {
210            /*
211             * Eat the longpress so it won't dismiss the recent apps dialog when
212             * the user lets go of the home key
213             */
214            mHomePressed = false;
215            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
216            showRecentAppsDialog();
217        }
218    };
219
220    /**
221     * Create (if necessary) and launch the recent apps dialog
222     */
223    private void showRecentAppsDialog() {
224        if (mRecentAppsDialog == null) {
225            mRecentAppsDialog = new RecentApplicationsDialog(mContext);
226        }
227        mRecentAppsDialog.show();
228    }
229
230    /** {@inheritDoc} */
231    public void init(Context context, IWindowManager windowManager,
232            LocalPowerManager powerManager) {
233        mContext = context;
234        mWindowManager = windowManager;
235        mPowerManager = powerManager;
236        mHandler = new Handler();
237        mShortcutManager = new ShortcutManager(context, mHandler);
238        mShortcutManager.observe();
239        mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
240        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
241        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
242                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
243        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
244        mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
245                "MidWindowManager.mBroadcastWakeLock");
246    }
247
248    /** {@inheritDoc} */
249    public int checkAddPermission(WindowManager.LayoutParams attrs) {
250        int type = attrs.type;
251        if (type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
252                || type > WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
253            return WindowManagerImpl.ADD_OKAY;
254        }
255        String permission = null;
256        switch (type) {
257            case TYPE_TOAST:
258                // XXX right now the app process has complete control over
259                // this...  should introduce a token to let the system
260                // monitor/control what they are doing.
261                break;
262            case TYPE_PHONE:
263            case TYPE_PRIORITY_PHONE:
264            case TYPE_SYSTEM_ALERT:
265            case TYPE_SYSTEM_ERROR:
266            case TYPE_SYSTEM_OVERLAY:
267                permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;
268                break;
269            default:
270                permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
271        }
272        if (permission != null) {
273            if (mContext.checkCallingOrSelfPermission(permission)
274                    != PackageManager.PERMISSION_GRANTED) {
275                return WindowManagerImpl.ADD_PERMISSION_DENIED;
276            }
277        }
278        return WindowManagerImpl.ADD_OKAY;
279    }
280
281    public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
282        switch (attrs.type) {
283            case TYPE_SYSTEM_OVERLAY:
284            case TYPE_TOAST:
285                // These types of windows can't receive input events.
286                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
287                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
288                break;
289        }
290    }
291
292    /** {@inheritDoc} */
293    public void adjustConfigurationLw(Configuration config) {
294        mPowerManager.setKeyboardVisibility(true);
295        config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
296        mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
297                LocalPowerManager.OTHER_EVENT);
298    }
299
300    public boolean isCheekPressedAgainstScreen(MotionEvent ev) {
301        return false;
302    }
303
304    /** {@inheritDoc} */
305    public int windowTypeToLayerLw(int type) {
306        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
307            return APPLICATION_LAYER;
308        }
309        switch (type) {
310        case TYPE_APPLICATION_PANEL:
311            return APPLICATION_LAYER;
312        case TYPE_APPLICATION_SUB_PANEL:
313            return APPLICATION_LAYER;
314        case TYPE_STATUS_BAR:
315            return STATUS_BAR_LAYER;
316        case TYPE_STATUS_BAR_PANEL:
317            return STATUS_BAR_PANEL_LAYER;
318        case TYPE_SEARCH_BAR:
319            return SEARCH_BAR_LAYER;
320        case TYPE_PHONE:
321            return PHONE_LAYER;
322        case TYPE_KEYGUARD:
323            return KEYGUARD_LAYER;
324        case TYPE_KEYGUARD_DIALOG:
325            return KEYGUARD_DIALOG_LAYER;
326        case TYPE_SYSTEM_ALERT:
327            return SYSTEM_ALERT_LAYER;
328        case TYPE_SYSTEM_ERROR:
329            return SYSTEM_ERROR_LAYER;
330        case TYPE_SYSTEM_OVERLAY:
331            return SYSTEM_OVERLAY_LAYER;
332        case TYPE_PRIORITY_PHONE:
333            return PRIORITY_PHONE_LAYER;
334        case TYPE_TOAST:
335            return TOAST_LAYER;
336        }
337        Log.e(TAG, "Unknown window type: " + type);
338        return APPLICATION_LAYER;
339    }
340
341    /** {@inheritDoc} */
342    public int subWindowTypeToLayerLw(int type) {
343        switch (type) {
344        case TYPE_APPLICATION_PANEL:
345            return APPLICATION_PANEL_SUBLAYER;
346        case TYPE_APPLICATION_MEDIA:
347            return APPLICATION_MEDIA_SUBLAYER;
348        case TYPE_APPLICATION_MEDIA_OVERLAY:
349            return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
350        case TYPE_APPLICATION_SUB_PANEL:
351            return APPLICATION_SUB_PANEL_SUBLAYER;
352        }
353        Log.e(TAG, "Unknown sub-window type: " + type);
354        return 0;
355    }
356
357    /** {@inheritDoc} */
358    public View addStartingWindow(IBinder appToken, String packageName,
359                                  int theme, CharSequence nonLocalizedLabel,
360                                  int labelRes, int icon) {
361        if (!SHOW_STARTING_ANIMATIONS) {
362            return null;
363        }
364        if (packageName == null) {
365            return null;
366        }
367
368    	Context context = mContext;
369    	boolean setTheme = false;
370    	//Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="
371    	//        + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));
372    	if (theme != 0 || labelRes != 0) {
373    	    try {
374    	        context = context.createPackageContext(packageName, 0);
375    	        if (theme != 0) {
376    	            context.setTheme(theme);
377    	            setTheme = true;
378    	        }
379    	    } catch (PackageManager.NameNotFoundException e) {
380    	    }
381    	}
382    	if (!setTheme) {
383    	    context.setTheme(com.android.internal.R.style.Theme);
384    	}
385
386        Window win = PolicyManager.makeNewWindow(context);
387        Resources r = context.getResources();
388        win.setTitle(r.getText(labelRes, nonLocalizedLabel));
389
390        win.setType(
391            WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
392        win.setFlags(
393            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
394            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
395            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
396            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
397
398        win.setLayout(WindowManager.LayoutParams.FILL_PARENT,
399                            WindowManager.LayoutParams.FILL_PARENT);
400
401        final WindowManager.LayoutParams params = win.getAttributes();
402        params.token = appToken;
403        params.packageName = packageName;
404        params.windowAnimations = win.getWindowStyle().getResourceId(
405                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
406        params.setTitle("Starting " + packageName);
407
408        try {
409            WindowManagerImpl wm = (WindowManagerImpl)
410                    context.getSystemService(Context.WINDOW_SERVICE);
411            View view = win.getDecorView();
412
413            if (win.isFloating()) {
414                // Whoops, there is no way to display an animation/preview
415                // of such a thing!  After all that work...  let's skip it.
416                // (Note that we must do this here because it is in
417                // getDecorView() where the theme is evaluated...  maybe
418                // we should peek the floating attribute from the theme
419                // earlier.)
420                return null;
421            }
422
423            if (localLOGV) Log.v(
424                TAG, "Adding starting window for " + packageName
425                + " / " + appToken + ": "
426                + (view.getParent() != null ? view : null));
427
428            wm.addView(view, params);
429
430            // Only return the view if it was successfully added to the
431            // window manager... which we can tell by it having a parent.
432            return view.getParent() != null ? view : null;
433        } catch (WindowManagerImpl.BadTokenException e) {
434            // ignore
435            Log.w(TAG, appToken + " already running, starting window not displayed");
436        }
437
438        return null;
439    }
440
441    /** {@inheritDoc} */
442    public void removeStartingWindow(IBinder appToken, View window) {
443        // RuntimeException e = new RuntimeException();
444        // Log.i(TAG, "remove " + appToken + " " + window, e);
445
446        if (localLOGV) Log.v(
447            TAG, "Removing starting window for " + appToken + ": " + window);
448
449        if (window != null) {
450            WindowManagerImpl wm = (WindowManagerImpl) mContext.getSystemService(Context.WINDOW_SERVICE);
451            wm.removeView(window);
452        }
453    }
454
455    /**
456     * Preflight adding a window to the system.
457     *
458     * Currently enforces that three window types are singletons:
459     * <ul>
460     * <li>STATUS_BAR_TYPE</li>
461     * <li>SEARCH_BAR_TYPE</li>
462     * <li>KEYGUARD_TYPE</li>
463     * </ul>
464     *
465     * @param win The window to be added
466     * @param attrs Information about the window to be added
467     *
468     * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons, WindowManagerImpl.ADD_MULTIPLE_SINGLETON
469     */
470    public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
471        switch (attrs.type) {
472            case TYPE_STATUS_BAR:
473                if (mStatusBar != null) {
474                    return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
475                }
476                mStatusBar = win;
477                break;
478            case TYPE_SEARCH_BAR:
479                if (mSearchBar != null) {
480                    return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
481                }
482                mSearchBar = win;
483                break;
484            case TYPE_KEYGUARD:
485                if (mKeyguard != null) {
486                    return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
487                }
488                mKeyguard = win;
489                break;
490        }
491        return WindowManagerImpl.ADD_OKAY;
492    }
493
494    /** {@inheritDoc} */
495    public void removeWindowLw(WindowState win) {
496        if (mStatusBar == win) {
497            mStatusBar = null;
498        }
499        else if (mSearchBar == win) {
500            mSearchBar = null;
501        }
502        else if (mKeyguard == win) {
503            mKeyguard = null;
504        }
505    }
506
507    private static final boolean PRINT_ANIM = false;
508
509    /** {@inheritDoc} */
510    public int selectAnimationLw(WindowState win, int transit) {
511        if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
512              + ": transit=" + transit);
513        if (transit == TRANSIT_PREVIEW_DONE) {
514            if (win.hasAppShownWindows()) {
515                if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT");
516                return com.android.internal.R.anim.app_starting_exit;
517            }
518        }
519
520        return 0;
521    }
522
523    private static IAudioService getAudioInterface() {
524        return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));
525    }
526
527    /** {@inheritDoc} */
528    public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down,
529            int repeatCount) {
530        if (false) {
531            Log.d(TAG, "interceptKeyTi code=" + code + " down=" + down + " repeatCount="
532                    + repeatCount);
533        }
534
535        // Clear a pending HOME longpress if the user releases Home
536        // TODO: This could probably be inside the next bit of logic, but that code
537        // turned out to be a bit fragile so I'm doing it here explicitly, for now.
538        if ((code == KeyEvent.KEYCODE_HOME) && !down) {
539            mHandler.removeCallbacks(mHomeLongPress);
540        }
541
542        // If the HOME button is currently being held, then we do special
543        // chording with it.
544        if (mHomePressed) {
545
546            // If we have released the home key, and didn't do anything else
547            // while it was pressed, then it is time to go home!
548            if (code == KeyEvent.KEYCODE_HOME) {
549                if (!down) {
550                    mHomePressed = false;
551                    launchHomeFromHotKey();
552                }
553            }
554
555            return true;
556        }
557
558        // First we always handle the home key here, so applications
559        // can never break it, although if keyguard is on, we do let
560        // it handle it, because that gives us the correct 5 second
561        // timeout.
562        if (code == KeyEvent.KEYCODE_HOME) {
563
564            // If a system window has focus, then it doesn't make sense
565            // right now to interact with applications.
566            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
567            if (attrs != null) {
568                int type = attrs.type;
569                if (type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
570                        && type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
571                    // Only do this once, so home-key-longpress doesn't close itself
572                    if (repeatCount == 0 && down) {
573                		sendCloseSystemWindows();
574                    }
575                    return false;
576                }
577            }
578
579            if (down && repeatCount == 0) {
580                mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
581                mHomePressed = true;
582            }
583            return true;
584        } else if (code == KeyEvent.KEYCODE_MENU) {
585            // Hijack modified menu keys for debugging features
586            final int chordBug = KeyEvent.META_SHIFT_ON;
587            final int chordProcess = KeyEvent.META_ALT_ON;
588
589            if (down && repeatCount == 0) {
590                if ((metaKeys & chordBug) == chordBug) {
591                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
592                    mContext.sendOrderedBroadcast(intent, null);
593                    return true;
594                } else if ((metaKeys & chordProcess) == chordProcess) {
595                    Intent service = new Intent();
596                    service.setClassName(mContext, "com.android.server.LoadAverageService");
597                    ContentResolver res = mContext.getContentResolver();
598                    boolean shown = Settings.System.getInt(
599                            res, Settings.System.SHOW_PROCESSES, 0) != 0;
600                    if (!shown) {
601                        mContext.startService(service);
602                    } else {
603                        mContext.stopService(service);
604                    }
605                    Settings.System.putInt(
606                            res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
607                    return true;
608                }
609            }
610        } else if (code == KeyEvent.KEYCODE_NOTIFICATION) {
611            if (down) {
612                // this key doesn't exist on current hardware, but if a device
613                // didn't have a touchscreen, it would want one of these to open
614                // the status bar.
615                IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
616                if (sbs != null) {
617                    try {
618                        sbs.toggle();
619                    } catch (RemoteException e) {
620                        // we're screwed anyway, since it's in this process
621                        throw new RuntimeException(e);
622                    }
623                }
624            }
625            return true;
626        } else if (code == KeyEvent.KEYCODE_SEARCH) {
627            if (down) {
628                if (repeatCount == 0) {
629                    mSearchKeyPressed = true;
630                }
631            } else {
632                mSearchKeyPressed = false;
633
634                if (mConsumeSearchKeyUp) {
635                    // Consume the up-event
636                    mConsumeSearchKeyUp = false;
637                    return true;
638                }
639            }
640        }
641
642        // Shortcuts are invoked through Search+key, so intercept those here
643        if (mSearchKeyPressed) {
644            if (down && repeatCount == 0) {
645                Intent shortcutIntent = mShortcutManager.getIntent(code, metaKeys);
646                if (shortcutIntent != null) {
647                    shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
648                    mContext.startActivity(shortcutIntent);
649
650                    /*
651                     * We launched an app, so the up-event of the search key
652                     * should be consumed
653                     */
654                    mConsumeSearchKeyUp = true;
655                    return true;
656                }
657            }
658        }
659
660        return false;
661    }
662
663    /**
664     * A home key -> launch home action was detected.  Take the appropriate action
665     * given the situation with the keyguard.
666     */
667    private void launchHomeFromHotKey() {
668        // no keyguard stuff to worry about, just launch home!
669        try {
670            ActivityManagerNative.getDefault().stopAppSwitches();
671        } catch (RemoteException e) {
672        }
673        mContext.startActivity(mHomeIntent);
674        sendCloseSystemWindows();
675    }
676
677    public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect coveredInset) {
678        final int fl = attrs.flags;
679
680        if ((fl &
681                (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
682                == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
683            coveredInset.set(mCurLeft, mCurTop, mW - mCurRight, mH - mCurBottom);
684        } else {
685            coveredInset.setEmpty();
686        }
687    }
688
689    /** {@inheritDoc} */
690    public void beginLayoutLw(int displayWidth, int displayHeight) {
691        mW = displayWidth;
692        mH = displayHeight;
693        mCurLeft = 0;
694        mCurTop = 0;
695        mCurRight = displayWidth;
696        mCurBottom = displayHeight;
697
698        // decide where the status bar goes ahead of time
699        if (mStatusBar != null) {
700            final Rect pf = mTmpParentFrame;
701            final Rect df = mTmpDisplayFrame;
702            final Rect vf = mTmpVisibleFrame;
703            pf.left = df.left = vf.left = 0;
704            pf.top = df.top = vf.top = 0;
705            pf.right = df.right = vf.right = displayWidth;
706            pf.bottom = df.bottom = vf.bottom = displayHeight;
707
708            mStatusBar.computeFrameLw(pf, df, vf, vf);
709            mCurTop = mStatusBar.getFrameLw().bottom;
710        }
711    }
712
713    /** {@inheritDoc} */
714    public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached) {
715        // we've already done the status bar
716        if (win == mStatusBar) {
717            return;
718        }
719
720        final int fl = attrs.flags;
721
722        final Rect pf = mTmpParentFrame;
723        final Rect df = mTmpDisplayFrame;
724        final Rect vf = mTmpVisibleFrame;
725
726        if ((fl &
727                (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
728                == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
729            df.left = 0;
730            df.top = 0;
731            df.right = mW;
732            df.bottom = mH;
733            vf.left = mCurLeft;
734            vf.top = mCurTop;
735            vf.right = mCurRight;
736            vf.bottom = mCurBottom;
737        } else if ((fl & FLAG_LAYOUT_IN_SCREEN) == 0) {
738            // Make sure this window doesn't intrude into the status bar.
739            df.left = vf.left = mCurLeft;
740            df.top = vf.top = mCurTop;
741            df.right = vf.right = mCurRight;
742            df.bottom = vf.bottom = mCurBottom;
743        } else {
744            df.left = vf.left = 0;
745            df.top = vf.top = 0;
746            df.right = vf.right = mW;
747            df.bottom = vf.bottom = mH;
748        }
749
750        if (attached != null && (fl & (FLAG_LAYOUT_IN_SCREEN)) == 0) {
751            pf.set(attached.getFrameLw());
752        } else {
753            pf.set(df);
754        }
755
756        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
757            df.left = df.top = vf.left = vf.top = -10000;
758            df.right = df.bottom = vf.right = vf.bottom = 10000;
759        }
760
761        win.computeFrameLw(pf, df, vf, vf);
762    }
763
764    /** {@inheritDoc} */
765    public boolean finishLayoutLw() {
766        return false;
767    }
768
769    /** {@inheritDoc} */
770    public void beginAnimationLw(int displayWidth, int displayHeight) {
771        mTopFullscreenOpaqueWindowState = null;
772        mForceStatusBar = false;
773    }
774
775    /** {@inheritDoc} */
776    public void animatingWindowLw(WindowState win,
777                                WindowManager.LayoutParams attrs) {
778        if (mTopFullscreenOpaqueWindowState == null
779            && attrs.type >= FIRST_APPLICATION_WINDOW
780            && attrs.type <= LAST_APPLICATION_WINDOW
781            && win.fillsScreenLw(mW, mH, true, false)
782            && win.isDisplayedLw()) {
783            mTopFullscreenOpaqueWindowState = win;
784        } else if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
785            mForceStatusBar = true;
786        }
787    }
788
789    /** {@inheritDoc} */
790    public boolean finishAnimationLw() {
791        if (mStatusBar != null) {
792            if (mForceStatusBar) {
793                mStatusBar.showLw(true);
794            } else if (mTopFullscreenOpaqueWindowState != null) {
795               WindowManager.LayoutParams lp =
796                   mTopFullscreenOpaqueWindowState.getAttrs();
797               boolean hideStatusBar =
798                   (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
799               if (hideStatusBar) {
800                   mStatusBar.hideLw(true);
801               } else {
802                   mStatusBar.showLw(true);
803               }
804           }
805        }
806       return false;
807    }
808
809    /** {@inheritDoc} */
810    public boolean preprocessInputEventTq(RawInputEvent event) {
811        return false;
812    }
813
814
815    /** {@inheritDoc} */
816    public boolean isAppSwitchKeyTqTiLwLi(int keycode) {
817        return keycode == KeyEvent.KEYCODE_HOME
818                || keycode == KeyEvent.KEYCODE_ENDCALL;
819    }
820
821    /** {@inheritDoc} */
822    public boolean isMovementKeyTi(int keycode) {
823        switch (keycode) {
824            case KeyEvent.KEYCODE_DPAD_UP:
825            case KeyEvent.KEYCODE_DPAD_DOWN:
826            case KeyEvent.KEYCODE_DPAD_LEFT:
827            case KeyEvent.KEYCODE_DPAD_RIGHT:
828                return true;
829        }
830        return false;
831    }
832
833    /**
834     * @return Whether music is being played right now.
835     */
836    private boolean isMusicActive() {
837        final IAudioService audio = getAudioInterface();
838        if (audio == null) {
839            Log.w(TAG, "isMusicActive: couldn't get IAudioService reference");
840            return false;
841        }
842        try {
843            return audio.isMusicActive();
844        } catch (RemoteException e) {
845            Log.w(TAG, "IAudioService.isMusicActive() threw RemoteException " + e);
846            return false;
847        }
848    }
849
850    /**
851     * Tell the audio service to adjust the volume appropriate to the event.
852     * @param keycode
853     */
854    private void sendVolToMusic(int keycode) {
855        final IAudioService audio = getAudioInterface();
856        if (audio == null) {
857            Log.w(TAG, "sendVolToMusic: couldn't get IAudioService reference");
858            return;
859        }
860        try {
861            // since audio is playing, we shouldn't have to hold a wake lock
862            // during the call, but we do it as a precaution for the rare possibility
863            // that the music stops right before we call this
864            mBroadcastWakeLock.acquire();
865            audio.adjustStreamVolume(
866                AudioManager.STREAM_MUSIC,
867                keycode == KeyEvent.KEYCODE_VOLUME_UP
868                            ? AudioManager.ADJUST_RAISE
869                            : AudioManager.ADJUST_LOWER,
870                    0);
871        } catch (RemoteException e) {
872            Log.w(TAG, "IAudioService.adjustStreamVolume() threw RemoteException " + e);
873        } finally {
874            mBroadcastWakeLock.release();
875        }
876    }
877
878    /** {@inheritDoc} */
879    public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) {
880        int result = ACTION_PASS_TO_USER | ACTION_POKE_USER_ACTIVITY;
881        return result;
882    }
883
884    class PassHeadsetKey implements Runnable {
885        KeyEvent mKeyEvent;
886
887        PassHeadsetKey(KeyEvent keyEvent) {
888            mKeyEvent = keyEvent;
889        }
890
891        public void run() {
892            Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
893            intent.putExtra(Intent.EXTRA_KEY_EVENT, mKeyEvent);
894            mContext.sendOrderedBroadcast(intent, null, mBroadcastDone,
895                    mHandler, Activity.RESULT_OK, null, null);
896        }
897    }
898
899    private BroadcastReceiver mBroadcastDone = new BroadcastReceiver() {
900        public void onReceive(Context context, Intent intent) {
901            mBroadcastWakeLock.release();
902        }
903    };
904
905    /** {@inheritDoc} */
906    public boolean isWakeRelMovementTq(int device, int classes,
907            RawInputEvent event) {
908        // if it's tagged with one of the wake bits, it wakes up the device
909        return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
910    }
911
912    /** {@inheritDoc} */
913    public boolean isWakeAbsMovementTq(int device, int classes,
914            RawInputEvent event) {
915        // if it's tagged with one of the wake bits, it wakes up the device
916        return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
917    }
918
919    /**
920     * Given the current state of the world, should this key wake up the device?
921     */
922    protected boolean isWakeKeyTq(RawInputEvent event) {
923        // There are not key maps for trackball devices, but we'd still
924        // like to have pressing it wake the device up, so force it here.
925        int keycode = event.keycode;
926        int flags = event.flags;
927        if (keycode == RawInputEvent.BTN_MOUSE) {
928            flags |= WindowManagerPolicy.FLAG_WAKE;
929        }
930        return (flags
931                & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
932    }
933
934    /** {@inheritDoc} */
935    public void screenTurnedOff(int why) {
936        EventLog.writeEvent(70000, 0);
937        mScreenOn = false;
938    }
939
940    /** {@inheritDoc} */
941    public void screenTurnedOn() {
942        EventLog.writeEvent(70000, 1);
943        mScreenOn = true;
944    }
945
946    /** {@inheritDoc} */
947    public void enableKeyguard(boolean enabled) {
948    }
949
950    /** {@inheritDoc} */
951    public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
952    }
953
954    /** {@inheritDoc} */
955    public boolean keyguardIsShowingTq() {
956        return false;
957    }
958
959    /** {@inheritDoc} */
960    public boolean inKeyguardRestrictedKeyInputMode() {
961        return false;
962    }
963
964    /**
965     * Callback from {@link KeyguardViewMediator}
966     */
967    public void onKeyguardShow() {
968        sendCloseSystemWindows();
969    }
970
971    private void sendCloseSystemWindows() {
972        sendCloseSystemWindows(null);
973    }
974
975    private void sendCloseSystemWindows(String reason) {
976        try {
977            ActivityManagerNative.getDefault().closeSystemDialogs(reason);
978        } catch (RemoteException e) {
979        }
980    }
981
982    public int rotationForOrientationLw(int orientation, int lastRotation,
983            boolean displayEnabled) {
984        // get rid of rotation for now. Always rotation of 0
985        return Surface.ROTATION_0;
986    }
987
988    public boolean detectSafeMode() {
989        try {
990            int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
991            mSafeMode = menuState > 0;
992            Log.i(TAG, "Menu key state: " + menuState + " safeMode=" + mSafeMode);
993            return mSafeMode;
994        } catch (RemoteException e) {
995            // Doom! (it's also local)
996            throw new RuntimeException("window manager dead");
997        }
998    }
999
1000    /** {@inheritDoc} */
1001    public void systemReady() {
1002        try {
1003            if (mSafeMode) {
1004                // If the user is holding the menu key code, then we are
1005                // going to boot into safe mode.
1006                ActivityManagerNative.getDefault().enterSafeMode();
1007            } else {
1008                // tell the keyguard
1009                android.os.SystemProperties.set("dev.bootcomplete", "1");
1010            }
1011        } catch (RemoteException e) {
1012        }
1013    }
1014
1015    /** {@inheritDoc} */
1016    public void enableScreenAfterBoot() {
1017        updateRotation();
1018    }
1019
1020    private void updateRotation() {
1021        mPowerManager.setKeyboardVisibility(true);
1022        int rotation = Surface.ROTATION_0;
1023        try {
1024            //set orientation on WindowManager
1025            mWindowManager.setRotation(rotation, true, 0);
1026        } catch (RemoteException e) {
1027        }
1028
1029        mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
1030                LocalPowerManager.OTHER_EVENT);
1031    }
1032
1033    /**
1034     * goes to the home screen
1035     * @return whether it did anything
1036     */
1037    boolean goHome() {
1038        if (false) {
1039            // This code always brings home to the front.
1040            try {
1041                ActivityManagerNative.getDefault().stopAppSwitches();
1042            } catch (RemoteException e) {
1043            }
1044            mContext.startActivity(mHomeIntent);
1045        } else {
1046            // This code brings home to the front or, if it is already
1047            // at the front, puts the device to sleep.
1048            try {
1049                ActivityManagerNative.getDefault().stopAppSwitches();
1050                int result = ActivityManagerNative.getDefault()
1051                        .startActivity(null, mHomeIntent,
1052                                mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
1053                                null, 0, null, null, 0, true /* onlyIfNeeded*/, false);
1054                if (result == IActivityManager.START_RETURN_INTENT_TO_CALLER) {
1055                    return false;
1056                }
1057            } catch (RemoteException ex) {
1058                // bummer, the activity manager, which is in this process, is dead
1059            }
1060        }
1061        sendCloseSystemWindows();
1062        return true;
1063    }
1064
1065    public void setCurrentOrientationLw(int newOrientation) {
1066        if(newOrientation != mCurrentAppOrientation) {
1067            mCurrentAppOrientation = newOrientation;
1068        }
1069    }
1070
1071    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
1072        return false;
1073    }
1074
1075    public void screenOnStoppedLw() {
1076    }
1077}
1078