/* * Copyright (C) 2014 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.systemui; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.Application; import android.app.usage.NetworkStats; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.media.AudioManager.AudioPlaybackCallback; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.util.ArraySet; import android.util.Log; import android.util.TimingsTraceLog; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HotspotControllerImpl; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.util.NotificationChannels; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; // AW:Added for BOOTEVENT import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; /* Add for disconnect Wi-Fi disconnect after screen off */ import android.app.AlarmManager; import android.app.PendingIntent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.BatteryManager; import android.provider.Settings; /** * Application class for SystemUI. */ public class SystemUIApplication extends Application implements SysUiServiceProvider { public static final String TAG = "SystemUIService"; private static final boolean DEBUG = false; /** * Hold a reference on the stuff we start. */ private SystemUI[] mServices; private String[] mNames; private boolean mServicesStarted; private boolean mBootCompleted; private HotspotControllerImpl mController; private final Map, Object> mComponents = new HashMap<>(); private final List mBaseStartServices = new ArrayList<>(); /* Add for disconnect Wi-Fi disconnect after screen off */ private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */ private static final long CHECK_WIFI_RX_MS = 60 * 1000; /* 1 minutes */ private static final long WIFI_RX_THRESHOLD = 64 * 1024; /* 64k bytes */ private static final String ACTION_DEVICE_IDLE = "com.android.WM.action.DEVICE_IDLE"; Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); private PendingIntent mIdleIntent; private boolean mScreenOff = false; private boolean mBatteryPlugged = false; private boolean mScheduleWifiDisconnect = false; private boolean mWifiDisconnectWhileSleep = false; private long mIdleMillis = DEFAULT_IDLE_MS; private long mTriggerTime = 0; private long mWifiUsage = 0; private long mAudioPlayTime = -1; private AudioPlaybackCallback mAudioPlaybackCallback = new AudioPlaybackCallback() { public void onPlaybackConfigChanged(List configs) { if (configs.size() == 0) { mAudioPlayTime = System.currentTimeMillis(); } else { mAudioPlayTime = 0; } } }; private String getProcessName(int pid) { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List list = am.getRunningAppProcesses(); Iterator i = list.iterator(); String myName = null; while (i.hasNext()) { ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo)(i.next()); try { if (info.pid == pid) { myName = info.processName; } } catch (Exception e) { e.printStackTrace(); } } return myName; } @Override public void onCreate() { super.onCreate(); // Set the application theme that is inherited by all services. Note that setting the // application theme in the manifest does only work for activities. Keep this in sync with // the theme set there. setTheme(R.style.Theme_SystemUI); SystemUIFactory.createFromConfig(this); if (!"com.android.systemui".equals(getProcessName(Process.myPid()))) return; if(SystemProperties.get("ro.product.platform").equals("homlet")){ Log.d(TAG,"is homlet"); mBaseStartServices.add("com.android.systemui.Dependency$DependencyCreator"); mBaseStartServices.add("com.android.systemui.ScreenDecorations"); mBaseStartServices.add("com.android.systemui.statusbar.CommandQueue$CommandQueueStart"); mBaseStartServices.add("com.android.systemui.SystemBars"); } else { mBaseStartServices.add("com.android.systemui.Dependency$DependencyCreator"); mBaseStartServices.add("com.android.systemui.ScreenDecorations"); mBaseStartServices.add("com.android.systemui.stackdivider.Divider"); mBaseStartServices.add("com.android.systemui.statusbar.CommandQueue$CommandQueueStart"); mBaseStartServices.add("com.android.systemui.keyguard.KeyguardViewMediator"); mBaseStartServices.add("com.android.systemui.SystemBars"); mBaseStartServices.add("com.android.systemui.recents.Recents"); mBaseStartServices.add("com.android.systemui.globalactions.GlobalActionsComponent"); } if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mBootCompleted) return; if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received"); unregisterReceiver(this); mBootCompleted = true; if (mServicesStarted) { final int N = mServices.length; for (int i = 0; i < N; i++) { long ti = System.currentTimeMillis(); if (mServices[i] == null) { String clsName = mNames[i]; if (DEBUG) Log.d(TAG, "loading: " + clsName); Class cls; try { cls = Class.forName(clsName); mServices[i] = (SystemUI) cls.newInstance(); } catch(ClassNotFoundException ex){ throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } mServices[i].mContext = SystemUIApplication.this; mServices[i].mComponents = mComponents; if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start(); } mServices[i].onBootCompleted(); ti = System.currentTimeMillis() - ti; Log.d("SystemUIBootTiming", "SystemUIService: bootcomplete " + mServices[i].getClass().getName() + " took " + ti + " ms"); } } } }, bootCompletedFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { if (!mBootCompleted) return; // Update names of SystemUi notification channels NotificationChannels.createAll(context); } } }, localeChangedFilter); } else { // We don't need to startServices for sub-process that is doing some tasks. // (screenshots, sweetsweetdesserts or tuner ..) String processName = ActivityThread.currentProcessName(); ApplicationInfo info = getApplicationInfo(); if (processName != null && processName.startsWith(info.processName + ":")) { return; } // For a secondary user, boot-completed will never be called because it has already // been broadcasted on startup for the primary SystemUI process. Instead, for // components which require the SystemUI component to be initialized per-user, we // start those components now for the current non-system user. startSecondaryUserServicesIfNeeded(); } ((AudioManager) getSystemService(Context.AUDIO_SERVICE)).registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(Looper.getMainLooper())); IntentFilter bootToostFilter = new IntentFilter(); // AW:turn off wifi and start after locked boot completed bootToostFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); bootToostFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); bootToostFilter.addAction(Intent.ACTION_SHUTDOWN); bootToostFilter.addAction(Intent.ACTION_SCREEN_OFF); bootToostFilter.addAction(Intent.ACTION_SCREEN_ON); bootToostFilter.addAction(Intent.ACTION_BATTERY_CHANGED); bootToostFilter.addAction(ACTION_DEVICE_IDLE); bootToostFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { boolean enable_wificontrol = getResources().getBoolean(R.bool.config_enable_screen_wifi_switch_control); WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); // AW:turn off wifi and start after locked boot completed if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) { Log.d(TAG,"Starting Wifi now"); boolean enable = Prefs.getBoolean(context, "wifiEnabled", false); if (enable) { wm.setWifiEnabled(true); Prefs.remove(context, "wifiEnabled"); } } else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { if (wm.isWifiEnabled()) { Prefs.putBoolean(context, "wifiEnabled", true); wm.setWifiEnabled(false); } } /* Enable/Disable Wi-Fi when screen on/off logic */ if (enable_wificontrol) { mController = new HotspotControllerImpl(context, new Handler(Looper.getMainLooper())); if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { boolean mApEnable = Prefs.getBoolean(context, "SoftapEnabled", false); boolean enable = Prefs.getBoolean(context, "wifiEnabled", false); if (mApEnable) { mController.setHotspotEnabled(true); Prefs.remove(context, "SoftapEnabled"); } if (enable) { wm.setWifiEnabled(true); Prefs.remove(context, "wifiEnabled"); } } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { if (wm.isWifiEnabled()) { Prefs.putBoolean(context, "wifiEnabled", true); wm.setWifiEnabled(false); } if (wm.isWifiApEnabled()) { Prefs.putBoolean(context, "SoftapEnabled", true); mController.setHotspotEnabled(false); } } return; } AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mIdleIntent = PendingIntent.getBroadcast(context, 0, idleIntent, 0); /* Handle disconnect Wi-Fi when screen off */ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { mScreenOff = false; if (mScheduleWifiDisconnect) { mScheduleWifiDisconnect = false; Log.d(TAG, "cancel schedule disconnect network"); mAlarmManager.cancel(mIdleIntent); } } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off * AND the "stay on while plugged in" setting doesn't match the * current power conditions (i.e, not plugged in, plugged in to USB, * or plugged in to AC). */ mScreenOff = true; mWifiDisconnectWhileSleep = false; mIdleMillis = Settings.Global.getLong(context.getContentResolver(), Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS); int mSleepPolicy = Settings.Global.getInt(context.getContentResolver(), Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_NEVER); if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_DEFAULT) || (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED && !mBatteryPlugged)) { mWifiDisconnectWhileSleep = true; } Log.d(TAG, "screen off, mWifiDisconnectWhileSleep: " + mWifiDisconnectWhileSleep); if (mWifiDisconnectWhileSleep && !mScheduleWifiDisconnect && mNetworkInfo.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) { mScheduleWifiDisconnect = true; mTriggerTime = System.currentTimeMillis() + mIdleMillis; Log.d(TAG, "schedule disconnect network after " + mIdleMillis + " ms"); mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, mTriggerTime, mIdleIntent); mWifiUsage = queryWifiUsage(context, 0, System.currentTimeMillis()); } } else if (ACTION_DEVICE_IDLE.equals(intent.getAction())) { boolean isMusic = (mAudioPlayTime == 0 || System.currentTimeMillis() - mAudioPlayTime < 1000); if (mWifiUsage > WIFI_RX_THRESHOLD || isMusic) { long usage = queryWifiUsage(context, 0, System.currentTimeMillis()); long inc = usage - mWifiUsage; Log.d(TAG, "wifi usage inc: " + inc + ", isMusic: " + isMusic); if (inc > WIFI_RX_THRESHOLD || isMusic) { mWifiUsage = usage; Log.d(TAG, "schedule disconnect network after " + CHECK_WIFI_RX_MS + " ms"); mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + CHECK_WIFI_RX_MS, mIdleIntent); return; } } Log.d(TAG, "alarm mWifiDisconnectWhileSleep and do disconnect"); mWifiUsage = 0; mScheduleWifiDisconnect = false; wm.disconnect(); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) { mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (mScreenOff && mWifiDisconnectWhileSleep && !mScheduleWifiDisconnect && mNetworkInfo.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) { if (System.currentTimeMillis() >= mTriggerTime) { mTriggerTime = System.currentTimeMillis() + 5000; Log.d(TAG, "alarm later after sceen off wifi connect event"); } mScheduleWifiDisconnect = true; Log.d(TAG, "schedule disconnect network after sceen off wifi connect event"); mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, mTriggerTime, mIdleIntent); } } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { int plugged = intent.getIntExtra("plugged", BatteryManager.BATTERY_PLUGGED_USB); if ((plugged & BatteryManager.BATTERY_PLUGGED_ANY) != 0) { mBatteryPlugged = true; } else { mBatteryPlugged = false; } } } }, bootToostFilter); } // query wifi download bytes private long queryWifiUsage(Context context, long start, long end) { long usage = -1; NetworkStatsManager nsm = context.getSystemService(NetworkStatsManager.class); try { final NetworkStats.Bucket bucket = nsm.querySummaryForDevice( ConnectivityManager.TYPE_WIFI, "", start, end); if (bucket != null) { usage = bucket.getRxBytes(); } } catch (RemoteException e) { Log.e(TAG, "Exception querying network detail.", e); } return usage; } // AW:Added for BOOTEVENT private static boolean sBootEventenable = SystemProperties.getBoolean("persist.sys.bootevent", true); static void logBootEvent(String bootevent) { if (!sBootEventenable) { return ; } FileOutputStream fos =null; try { fos = new FileOutputStream("/proc/bootevent"); fos.write(bootevent.getBytes()); fos.flush(); } catch (FileNotFoundException e) { Log.e("BOOTEVENT","Failure open /proc/bootevent,not found!",e); } catch (java.io.IOException e) { Log.e("BOOTEVENT","Failure open /proc/bootevent entry",e); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { Log.e ("BOOTEVENT","Failure close /proc/bootevent entry",e); } } } } /** * Makes sure that all the SystemUI services are running. If they are already running, this is a * no-op. This is needed to conditinally start all the services, as we only need to have it in * the main process. *

This method must only be called from the main thread.

*/ public void startServicesIfNeeded() { String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); startServicesIfNeeded(names); } /** * Ensures that all the Secondary user SystemUI services are running. If they are already * running, this is a no-op. This is needed to conditinally start all the services, as we only * need to have it in the main process. *

This method must only be called from the main thread.

*/ void startSecondaryUserServicesIfNeeded() { String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); startServicesIfNeeded(names); } private void startServicesIfNeeded(String[] services) { if (mServicesStarted) { return; } mNames = services; mServices = new SystemUI[services.length]; boolean enableKeyguardService = getResources().getBoolean(R.bool.config_enableKeyguardService); Log.d(TAG,"enableKeyguardService=" + enableKeyguardService); if (!mBootCompleted) { // check to see if maybe it was already completed long before we began // see ActivityManagerService.finishBooting() if ("1".equals(SystemProperties.get("sys.boot_completed"))) { mBootCompleted = true; if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent"); } } // AW:Added for BOOTEVENT logBootEvent("SystemUIService:Starting SystemUI services"); Log.v(TAG, "Starting SystemUI services for user " + Process.myUserHandle().getIdentifier() + "."); TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", Trace.TRACE_TAG_APP); log.traceBegin("StartServices"); final int N = services.length; for (int i = 0; i < N; i++) { String clsName = services[i]; if (!mBootCompleted && !mBaseStartServices.contains(clsName)) { continue; } if(!enableKeyguardService && clsName.equals("com.android.systemui.SystemBars") && !mBootCompleted) { logBootEvent("com.android.systemui.SystemBars will start later"); continue; } if (DEBUG) Log.d(TAG, "loading: " + clsName); log.traceBegin("StartServices" + clsName); long ti = System.currentTimeMillis(); Class cls; try { cls = Class.forName(clsName); Object o = cls.newInstance(); if (o instanceof SystemUI.Injector) { o = ((SystemUI.Injector) o).apply(this); } mServices[i] = (SystemUI) o; } catch(ClassNotFoundException ex){ throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } mServices[i].mContext = this; mServices[i].mComponents = mComponents; if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start(); log.traceEnd(); // Warn if initialization of component takes too long ti = System.currentTimeMillis() - ti; if (ti > 1000) { Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms"); } if (ti > 30) { // AW:Added for BOOTEVENT logBootEvent("SystemUIService: running " + cls.getName() + " took " + ti + " ms"); } if (mBootCompleted) { mServices[i].onBootCompleted(); } } Dependency.get(InitController.class).executePostInitTasks(); log.traceEnd(); final Handler mainHandler = new Handler(Looper.getMainLooper()); Dependency.get(PluginManager.class).addPluginListener( new PluginListener() { private ArraySet mOverlays = new ArraySet<>(); @Override public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { mainHandler.post(new Runnable() { @Override public void run() { StatusBar statusBar = getComponent(StatusBar.class); if (statusBar != null) { plugin.setup(statusBar.getStatusBarWindow(), statusBar.getNavigationBarView(), new Callback(plugin)); } } }); } @Override public void onPluginDisconnected(OverlayPlugin plugin) { mainHandler.post(new Runnable() { @Override public void run() { mOverlays.remove(plugin); Dependency.get(StatusBarWindowController.class).setForcePluginOpen( mOverlays.size() != 0); } }); } class Callback implements OverlayPlugin.Callback { private final OverlayPlugin mPlugin; Callback(OverlayPlugin plugin) { mPlugin = plugin; } @Override public void onHoldStatusBarOpenChange() { if (mPlugin.holdStatusBarOpen()) { mOverlays.add(mPlugin); } else { mOverlays.remove(mPlugin); } mainHandler.post(new Runnable() { @Override public void run() { Dependency.get(StatusBarWindowController.class) .setStateListener(b -> mOverlays.forEach( o -> o.setCollapseDesired(b))); Dependency.get(StatusBarWindowController.class) .setForcePluginOpen(mOverlays.size() != 0); } }); } } }, OverlayPlugin.class, true /* Allow multiple plugins */); mServicesStarted = true; } @Override public void onConfigurationChanged(Configuration newConfig) { if (mServicesStarted) { int len = mServices.length; for (int i = 0; i < len; i++) { if (mServices[i] != null) { mServices[i].onConfigurationChanged(newConfig); } } } } @SuppressWarnings("unchecked") public T getComponent(Class interfaceType) { return (T) mComponents.get(interfaceType); } public SystemUI[] getServices() { return mServices; } }