Usage of AlertDialog [ AlertDialog.Builder(Activity) ]

对话框组件使用教程
本文介绍了如何使用Android中的对话框组件AlertDialog。通过为按钮添加监听器并在其onClick方法中触发对话框,可以实现显示标题、消息及按钮的功能。具体包括设置对话框的标题、消息内容及确认按钮。

=============================================

Download project link: http://ishare.iask.sina.com.cn/f/33385525.html

Included Files are :
AlertDialog.apk
AlertDialog.zip(source code)
result.png
result_button.png
result_message.png
result_title.png

=============================================

To trigger a dialog ,we add a listener on a button and show it in its OnClick method.

①show title in dialog.

We just need to use the method of the AlertDialog.Builder : “.setTitle”

clip_image001

show Message in dialog.

We just need to call the method of the AlertDialog.Buider: “.setMessage”

clip_image002

show a Button in dialog

We need to call the method of the AlertDialog.Builder:”.setPostivieButton”.

Note that there are two parameters in this method: text and listener.

clip_image004

clip_image005

clip_image007

clip_image009clip_image011clip_image013

clip_image015

package com.hik.netsdk.SimpleDemo.View; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Intent; import android.graphics.PixelFormat; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; // AndroidX Media3 导入 import androidx.media3.common.MediaItem; import androidx.media3.common.Player; import androidx.media3.common.PlaybackException; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DefaultHttpDataSource; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.hls.HlsMediaSource; import androidx.media3.exoplayer.source.MediaSource; import com.hik.netsdk.SimpleDemo.R; //import com.hik.netsdk.SimpleDemo.View.DevMgtUI.AddDevActivity; import com.videogo.openapi.EZConstants; import com.videogo.openapi.EZOpenSDK; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; // 来自 okhttp3 import okhttp3.RequestBody; import okhttp3.Response; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.okhttp.OkHttpDataSource; import okhttp3.OkHttpClient; import androidx.media3.datasource.okhttp.OkHttpDataSource; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback { // 核心参数 private String mDeviceSerial; private String mVerifyCode; private int mCameraNo = 1; private boolean isEzDevice = false; // 萤石云相关参数 private String mAppKey = "a794d58c13154caeb7d2fbb5c3420c65"; private String mAccessToken = ""; // UI组件 private SurfaceView mPreviewSurface; private SurfaceHolder mSurfaceHolder; private Toolbar m_toolbar; private ProgressBar mProgressBar; private RelativeLayout mControlLayout; private ImageButton mRotateButton; // 播放器相关 private ExoPlayer mExoPlayer; private String mPlaybackUrl; private final OkHttpClient mHttpClient = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS) .build(); // 播放器状态监听器 private final Player.Listener mPlayerListener = new Player.Listener() { @Override public void onPlaybackStateChanged(int state) { switch (state) { case Player.STATE_READY: mProgressBar.setVisibility(View.GONE); Log.d("ExoPlayer", "播放准备就绪"); break; case Player.STATE_BUFFERING: mProgressBar.setVisibility(View.VISIBLE); Log.d("ExoPlayer", "缓冲中..."); break; case Player.STATE_ENDED: Log.d("ExoPlayer", "播放结束"); break; case Player.STATE_IDLE: Log.d("ExoPlayer", "空闲状态"); break; } } @Override public void onPlayerError(PlaybackException error) { // 使用正确的异常类型 Log.e("ExoPlayer", "播放错误: " + error.getMessage()); handleError("播放错误: " + error.getMessage(), -1); } }; // 创建 OkHttp 客户端 OkHttpClient okHttpClient = new OkHttpClient(); // 创建数据源工厂 DataSource.Factory dataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) mHttpClient) .setUserAgent("@string/app_name"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitymain); // 初始化UI组件 initUIComponents(); // 获取启动参数 parseIntentParams(); // 初始化SDK initSDK(); // 参数校验 if (mDeviceSerial == null || mDeviceSerial.isEmpty()) { showErrorAndFinish("设备序列号不能为空"); return; } Log.d("MainActivity", "onCreate完成: isEzDevice=" + isEzDevice); } @SuppressLint("WrongViewCast") private void initUIComponents() { // 基础UI m_toolbar = findViewById(R.id.titlename); setSupportActionBar(m_toolbar); // 预览相关UI mPreviewSurface = findViewById(R.id.realplay_sv); mSurfaceHolder = mPreviewSurface.getHolder(); mSurfaceHolder.addCallback(this); mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT); mProgressBar = findViewById(R.id.liveProgressBar); mControlLayout = findViewById(R.id.rl_control); mRotateButton = findViewById(R.id.ib_rotate2); // 设置旋转按钮点击事件 mRotateButton.setOnClickListener(v -> changeScreen()); // 隐藏管理UI // DrawerLayout drawer = findViewById(R.id.drawer_layout); // if (drawer != null) { // drawer.setVisibility(View.GONE); // } if (m_toolbar != null) { m_toolbar.setVisibility(View.GONE); } // 初始化控制按钮 initControlButtons(); Log.d("UI", "UI组件初始化完成"); } private void parseIntentParams() { // 获取Intent参数 Intent intent = getIntent(); isEzDevice = true; // 只支持萤石设备 mAccessToken = intent.getStringExtra("accessToken"); // 优先使用bundle Bundle bundle = intent.getExtras(); if (bundle != null) { mDeviceSerial = bundle.getString("devSn"); mVerifyCode = bundle.getString("verifyCode"); mCameraNo = bundle.getInt("cameraNo", 1); } // 兼容直接Extra方式 if (mDeviceSerial == null) mDeviceSerial = intent.getStringExtra("devSn"); if (mVerifyCode == null) mVerifyCode = intent.getStringExtra("verifyCode"); if (mCameraNo == 0) mCameraNo = intent.getIntExtra("cameraNo", 1); Log.d("Params", "设备序列号: " + mDeviceSerial + ", 通道号: " + mCameraNo); } private void initSDK() { try { // 使用反射检查初始化状态 boolean isInitialized = false; try { // 尝试获取实例 EZOpenSDK instance = EZOpenSDK.getInstance(); if (instance != null) { isInitialized = true; } } catch (Exception e) { // 捕获未初始化的异常 isInitialized = false; } // 未初始化时进行初始化 if (!isInitialized) { EZOpenSDK.initLib(getApplication(), mAppKey); Log.d("EZSDK", "萤石SDK初始化完成"); } // 设置AccessToken if (mAccessToken != null && !mAccessToken.isEmpty()) { EZOpenSDK.getInstance().setAccessToken(mAccessToken); Log.d("EZToken", "AccessToken设置成功"); } else { Log.w("EZToken", "AccessToken缺失!"); } } catch (Exception e) { Log.e("EZSDK", "萤石SDK初始化失败", e); handleError("萤石SDK初始化失败: " + e.getMessage(), -1); } } private void initControlButtons() { // 云台控制按钮 findViewById(R.id.ptz_top_btn).setOnClickListener(v -> controlPTZ("UP")); findViewById(R.id.ptz_bottom_btn).setOnClickListener(v -> controlPTZ("DOWN")); findViewById(R.id.ptz_left_btn).setOnClickListener(v -> controlPTZ("LEFT")); findViewById(R.id.ptz_right_btn).setOnClickListener(v -> controlPTZ("RIGHT")); // 变焦控制 findViewById(R.id.focus_add).setOnClickListener(v -> controlZoom("ZOOM_IN")); findViewById(R.id.foucus_reduce).setOnClickListener(v -> controlZoom("ZOOM_OUT")); // 水平布局控制按钮 findViewById(R.id.ptz_top_btn2).setOnClickListener(v -> controlPTZ("UP")); findViewById(R.id.ptz_bottom_btn2).setOnClickListener(v -> controlPTZ("DOWN")); findViewById(R.id.ptz_left_btn2).setOnClickListener(v -> controlPTZ("LEFT")); findViewById(R.id.ptz_right_btn2).setOnClickListener(v -> controlPTZ("RIGHT")); // 添加萤石云特有功能按钮 // findViewById(R.id.btn_record).setOnClickListener(v -> startLocalRecord()); // findViewById(R.id.btn_stop_record).setOnClickListener(v -> stopLocalRecord()); // findViewById(R.id.btn_capture).setOnClickListener(v -> capturePicture()); Log.d("Controls", "控制按钮初始化完成"); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.d("Surface", "Surface created"); // 获取播放地址并开始播放 new Thread(this::fetchPlaybackUrl).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d("Surface", "Surface changed: " + width + "x" + height); if (mExoPlayer != null) { mExoPlayer.setVideoSurfaceHolder(holder); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d("Surface", "Surface destroyed"); stopPreview(); } // ======================== 播放地址获取与播放 ======================== private void fetchPlaybackUrl() { try { // 构建请求参数 JSONObject params = new JSONObject(); params.put("accessToken", mAccessToken); params.put("deviceSerial", mDeviceSerial); params.put("channelNo", mCameraNo); params.put("protocol", 2); // 使用HLS协议 if (mVerifyCode != null && !mVerifyCode.isEmpty()) { params.put("code", mVerifyCode); } params.put("expireTime", 7200); // 2小时有效期 // 创建请求体 RequestBody body = RequestBody.create( MediaType.parse("application/json"), params.toString() ); // 创建请求 Request request = new Request.Builder() .url("https://open.ys7.com/api/lapp/v2/live/address/get") .post(body) .build(); // 执行请求 try (Response response = mHttpClient.newCall(request).execute()) { if (!response.isSuccessful()) { handleError("获取播放地址失败: " + response.code(), response.code()); return; } // 解析响应 String responseBody = response.body().string(); JSONObject json = new JSONObject(responseBody); if ("200".equals(json.getString("code"))) { JSONObject data = json.getJSONObject("data"); mPlaybackUrl = data.getString("url"); Log.d("PlaybackURL", "获取到播放地址: " + mPlaybackUrl); // 在主线程初始化播放器 runOnUiThread(this::initExoPlayer); } else { handleError("API错误: " + json.getString("msg"), json.getInt("code")); } } } catch (JSONException | IOException e) { Log.e("FetchURL", "获取播放地址异常", e); handleError("获取播放地址异常: " + e.getMessage(), -1); } } private void initExoPlayer() { // 释放现有播放器 if (mExoPlayer != null) { mExoPlayer.release(); } // 使用正确的 Builder 创建播放器 mExoPlayer = new ExoPlayer.Builder(this).build(); mExoPlayer.addListener(mPlayerListener); // 设置视频渲染器 mExoPlayer.setVideoSurfaceHolder(mSurfaceHolder); // 创建媒体源 DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory(); MediaSource mediaSource = new HlsMediaSource.Factory(dataSourceFactory) .createMediaSource(MediaItem.fromUri(Uri.parse(mPlaybackUrl))); // 准备播放 mExoPlayer.setMediaSource(mediaSource); mExoPlayer.prepare(); mExoPlayer.setPlayWhenReady(true); mProgressBar.setVisibility(View.VISIBLE); Log.d("ExoPlayer", "播放器初始化完成,开始播放"); } // ======================== 萤石云控制方法 ======================== private int mapDirection(String direction) { switch (direction) { // case "UP": return EZCloudControl.DIRECTION_UP; // case "DOWN": return EZCloudControl.DIRECTION_DOWN; // case "LEFT": return EZCloudControl.DIRECTION_LEFT; // case "RIGHT": return EZCloudControl.DIRECTION_RIGHT; // case "ZOOM_IN": return EZCloudControl.DIRECTION_ZOOM_IN; // case "ZOOM_OUT": return EZCloudControl.DIRECTION_ZOOM_OUT; default: return -1; } } // 修改控制方法 private void controlPTZ(String direction) { int directionCode = mapDirection(direction); if (directionCode == -1) return; // 开始控制 // EZCloudControl.startPTZControl( // mAccessToken, // mDeviceSerial, // mCameraNo, // directionCode, // EZCloudControl.SPEED_MEDIUM, // 中等速度 // new EZCloudControl.ControlCallback() { // @Override // public void onSuccess(String response) { // Log.d("PTZControl", "开始云台控制成功: " + direction); // // // 300ms后停止控制 // new Handler(Looper.getMainLooper()).postDelayed(() -> { // stopPTZControl(directionCode); // }, 300); // } // // @Override // public void onFailure(String error) { // Log.e("PTZControl", "开始云台控制失败: " + error); // handleError("云台控制失败: " + error, -1); // } // }); } // private void stopPTZControl(int directionCode) { // EZCloudControl.stopPTZControl( // mAccessToken, // mDeviceSerial, // mCameraNo, // directionCode, // new EZCloudControl.ControlCallback() { // @Override // public void onSuccess(String response) { // Log.d("PTZControl", "停止云台控制成功"); // } // // @Override // public void onFailure(String error) { // Log.e("PTZControl", "停止云台控制失败: " + error); // // 可选择重试或其他处理 // } // }); // } private void controlZoom(String command) { String direction = "ZOOM_IN".equals(command) ? "ZOOM_IN" : "ZOOM_OUT"; controlPTZ(direction); } // ======================== 萤石云扩展功能 ======================== // 开始本地录像 private void startLocalRecord() { // 这里需要根据实际需求实现 Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show(); } // 停止本地录像 private void stopLocalRecord() { // 这里需要根据实际需求实现 Toast.makeText(this, "停止录像", Toast.LENGTH_SHORT).show(); } // 截图 private void capturePicture() { // 这里需要根据实际需求实现 Toast.makeText(this, "截图已保存", Toast.LENGTH_SHORT).show(); } // ======================== 通用功能方法 ======================== public void changeScreen(View view) { changeScreen(); } private void changeScreen() { if (mControlLayout.getVisibility() == View.VISIBLE) { mControlLayout.setVisibility(View.GONE); } else { mControlLayout.setVisibility(View.VISIBLE); } } private void handleError(String message, int errorCode) { String fullMessage = message + " (错误码: " + errorCode + ")"; // 萤石云特有错误码处理 switch (errorCode) { case 400001: fullMessage = "AccessToken无效"; break; case 400002: fullMessage = "设备不存在"; break; case 400007: fullMessage = "设备不在线"; break; case 400034: fullMessage = "验证码错误"; break; case 400035: fullMessage = "设备已被自己添加"; break; case 400036: fullMessage = "设备已被别人添加"; break; default: fullMessage = "萤石云错误: " + errorCode; } new AlertDialog.Builder(this) .setTitle("预览失败") .setMessage(fullMessage) .setPositiveButton("确定", (d, w) -> finish()) .setCancelable(false) .show(); } private void showErrorAndFinish(String message) { Toast.makeText(this, message, Toast.LENGTH_LONG).show(); new Handler().postDelayed(this::finish, 3000); } private void stopPreview() { if (mExoPlayer != null) { mExoPlayer.release(); mExoPlayer = null; Log.d("Preview", "预览已停止"); } } @Override protected void onDestroy() { super.onDestroy(); stopPreview(); Log.d("Lifecycle", "onDestroy"); } @Override protected void onPause() { super.onPause(); if (mExoPlayer != null) { mExoPlayer.setPlayWhenReady(false); Log.d("Lifecycle", "暂停播放"); } } @Override protected void onResume() { super.onResume(); if (mExoPlayer != null) { mExoPlayer.setPlayWhenReady(true); Log.d("Lifecycle", "恢复播放"); } } // @Override // public void onBackPressed() { // DrawerLayout drawer = findViewById(R.id.drawer_layout); // if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) { // drawer.closeDrawer(GravityCompat.START); // } else { // super.onBackPressed(); // } // } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main_opt, menu); return true; } // @Override // public boolean onOptionsItemSelected(MenuItem item) { // if (item.getItemId() == R.id.action_1) { // startActivity(new Intent(this, AddDevActivity.class)); // return true; // } // return super.onOptionsItemSelected(item); // } } 依据上述代码解决报错:Failed to read `level` from `@androidx.media3.common.util.UnstableApi` -- assuming `ERROR`. This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)` Failed to read `level` from `@androidx.media3.common.util.UnstableApi` -- assuming `ERROR`. This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)` Failed to read `level` from `@androidx.media3.common.util.UnstableApi` -- assuming `ERROR`. This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)` Failed to read `level` from `@androidx.media3.common.util.UnstableApi` -- assuming `ERROR`. This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`
06-23
package com.android.server.power; 19 20 import android.app.ActivityManagerInternal; 21 import android.app.AlertDialog; 22 import android.app.BroadcastOptions; 23 import android.app.Dialog; 24 import android.app.IActivityManager; 25 import android.app.ProgressDialog; 26 import android.app.admin.SecurityLog; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.IIntentReceiver; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManagerInternal; 34 import android.content.pm.ActivityInfo; 35 import android.os.Bundle; 36 import android.os.FileUtils; 37 import android.os.Handler; 38 import android.os.PowerManager; 39 import android.os.RecoverySystem; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.os.SystemClock; 43 import android.os.SystemProperties; 44 import android.os.SystemVibrator; 45 import android.os.Trace; 46 import android.os.UserHandle; 47 import android.os.UserManager; 48 import android.os.VibrationAttributes; 49 import android.os.VibrationEffect; 50 import android.os.Vibrator; 51 import android.os.vibrator.persistence.VibrationXmlParser; 52 import android.telephony.TelephonyManager; 53 import android.text.TextUtils; 54 import android.util.ArrayMap; 55 import android.util.Log; 56 import android.util.Slog; 57 import android.util.TimingsTraceLog; 58 import android.view.SurfaceControl; 59 import android.view.WindowManager; 60 import android.view.IWindowManager; 61 import android.view.Surface; 62 import com.android.server.pm.OplusDLCUtils; 63 64 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT 65 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 66 import java.io.FileWriter; 67 import libcore.io.IoUtils; 68 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */ 69 70 import com.android.internal.annotations.VisibleForTesting; 71 import com.android.server.LocalServices; 72 import com.android.server.RescueParty; 73 import com.android.server.statusbar.StatusBarManagerInternal; 74 75 import java.io.File; 76 import java.io.FileOutputStream; 77 import java.io.FileReader; 78 import java.io.IOException; 79 import java.nio.charset.StandardCharsets; 80 81 public final class ShutdownThread extends Thread { 82 // constants 83 private static final boolean DEBUG = true; 84 private static final String TAG = "ShutdownThread"; 85 private static final int ACTION_DONE_POLL_WAIT_MS = 500; 86 private static final int RADIOS_STATE_POLL_SLEEP_MS = 100; 87 // maximum time we wait for the shutdown broadcast before going on. 88 private static final int MAX_BROADCAST_TIME = 10 * 1000; 89 private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 10 * 1000; 90 private static final int MAX_RADIO_WAIT_TIME = 12 * 1000; 91 private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000; 92 // constants for progress bar. the values are roughly estimated based on timeout. 93 private static final int BROADCAST_STOP_PERCENT = 2; 94 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4; 95 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6; 96 private static final int RADIO_STOP_PERCENT = 18; 97 private static final int MOUNT_SERVICE_STOP_PERCENT = 20; 98 // Time we should wait for vendor subsystem shutdown 99 // Sleep times(ms) between checks of the vendor subsystem state 100 private static final int VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS = 100; 101 // Max time we wait for vendor subsystems to shut down before resuming 102 // with full system shutdown 103 private static final int VENDOR_SUBSYS_MAX_WAIT_MS = 10000; 104 105 // length of vibration before shutting down 106 @VisibleForTesting static final int DEFAULT_SHUTDOWN_VIBRATE_MS = 500; 107 108 // state tracking 109 private static final Object sIsStartedGuard = new Object(); 110 private static boolean sIsStarted = false; 111 112 private static boolean mReboot; 113 private static boolean mRebootSafeMode; 114 private static boolean mRebootHasProgressBar; 115 private static String mReason; 116 117 // Provides shutdown assurance in case the system_server is killed 118 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; 119 120 // Indicates whether we are rebooting into safe mode 121 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode"; 122 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode"; 123 124 // static instance of this thread 125 private static final ShutdownThread sInstance = new ShutdownThread(); 126 127 // Metrics that will be reported to tron after reboot 128 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>(); 129 130 // File to use for saving shutdown metrics 131 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics"; 132 // File to use for saving shutdown check points 133 private static final String CHECK_POINTS_FILE_BASENAME = 134 "/data/system/shutdown-checkpoints/checkpoints"; 135 136 private static final int MIN_SHUTDOWN_ANIMATION_PLAY_TIME = 5*1000; 137 private static long beginAnimationTime = 0; 138 private static long endAnimationTime = 0; 139 140 // Metrics names to be persisted in shutdown-metrics file 141 private static String METRIC_SYSTEM_SERVER = "shutdown_system_server"; 142 private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast"; 143 private static String METRIC_AM = "shutdown_activity_manager"; 144 private static String METRIC_PM = "shutdown_package_manager"; 145 private static String METRIC_RADIOS = "shutdown_radios"; 146 private static String METRIC_RADIO = "shutdown_radio"; 147 private static String METRIC_SHUTDOWN_TIME_START = "begin_shutdown"; 148 149 private final Injector mInjector; 150 151 private final Object mActionDoneSync = new Object(); 152 private boolean mActionDone; 153 private Context mContext; 154 private PowerManager mPowerManager; 155 private PowerManager.WakeLock mCpuWakeLock; 156 private PowerManager.WakeLock mScreenWakeLock; 157 private Handler mHandler; 158 159 private static AlertDialog sConfirmDialog; 160 private ProgressDialog mProgressDialog; 161 162 private static final String PROP_IS_MONKEYKING_RUNNING = "oplus.autotest.monkeyRunning"; 163 private static final String PROP_IS_MONKEY_RUNNING = "oppo.autotest.monkeyRunning"; 164 private static final String PROP_IS_AGINGVERSION = "ro.build.aging_version"; 165 166 private ShutdownThread() { 167 this(new Injector()); 168 } 169 170 @VisibleForTesting 171 ShutdownThread(Injector injector) { 172 mInjector = injector; 173 } 174 175 /** 176 * Request a clean shutdown, waiting for subsystems to clean up their 177 * state etc. Must be called from a Looper thread in which its UI 178 * is shown. 179 * 180 * @param context Context used to display the shutdown progress dialog. This must be a context 181 * suitable for displaying UI (aka Themable). 182 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. 183 * @param confirm true if user confirmation is needed before shutting down. 184 */ 185 public static void shutdown(final Context context, String reason, boolean confirm) { 186 mReboot = false; 187 mRebootSafeMode = false; 188 mReason = reason; 189 shutdownInner(context, confirm); 190 } 191 192 private static void shutdownInner(final Context context, boolean confirm) { 193 // ShutdownThread is called from many places, so best to verify here that the context passed 194 // in is themed. 195 context.assertRuntimeOverlayThemable(); 196 197 // ensure that only one thread is trying to power down. 198 // any additional calls are just returned 199 synchronized (sIsStartedGuard) { 200 if (sIsStarted) { 201 if (DEBUG) { 202 Log.d(TAG, "Request to shutdown already running, returning."); 203 } 204 return; 205 } 206 } 207 208 // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but 209 // this point preserves the system trace of the trigger point of the ShutdownThread. 210 ShutdownCheckPoints.recordCheckPoint(/* reason= */ null); 211 212 //#ifdef ODM_WT_EDIT Gaohan.Wang@ODM_WT.AD, 2023/12/6 Add for Monkey call emergency, rebootPhone....... 213 if (isAgingTestNotAllowShutdown(mReason)) { 214 return; 215 } 216 //#endif ODM_WT_EDIT 217 218 final int longPressBehavior = context.getResources().getInteger( 219 com.android.internal.R.integer.config_longPressOnPowerBehavior); 220 final int resourceId = mRebootSafeMode 221 ? com.android.internal.R.string.reboot_safemode_confirm 222 : (longPressBehavior == 2 223 ? com.android.internal.R.string.shutdown_confirm_question 224 : com.android.internal.R.string.shutdown_confirm); 225 226 if (DEBUG) { 227 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); 228 } 229 230 if (confirm) { 231 final CloseDialogReceiver closer = new CloseDialogReceiver(context); 232 if (sConfirmDialog != null) { 233 sConfirmDialog.dismiss(); 234 } 235 sConfirmDialog = new AlertDialog.Builder(context) 236 .setTitle(mRebootSafeMode 237 ? com.android.internal.R.string.reboot_safemode_title 238 : com.android.internal.R.string.power_off) 239 .setMessage(resourceId) 240 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { 241 public void onClick(DialogInterface dialog, int which) { 242 beginShutdownSequence(context); 243 } 244 }) 245 .setNegativeButton(com.android.internal.R.string.no, null) 246 .create(); 247 closer.dialog = sConfirmDialog; 248 sConfirmDialog.setOnDismissListener(closer); 249 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 250 sConfirmDialog.show(); 251 } else { 252 beginShutdownSequence(context); 253 } 254 } 255 256 public static boolean isAgingTestNotAllowShutdown(String reason) { 257 boolean isAgingTest = "yes".equals(SystemProperties.get(PROP_IS_AGINGVERSION)); 258 boolean isMonkeyRunning = SystemProperties.getBoolean(PROP_IS_MONKEY_RUNNING, false); 259 boolean isMonkeyKingRunning = SystemProperties.getBoolean(PROP_IS_MONKEYKING_RUNNING, false); 260 Slog.d(TAG, " reason=" + reason 261 + ", isAgingTest=" + isAgingTest 262 + ", isMonkeyRunning=" + isMonkeyRunning 263 + ", isMonkeyKingRunning=" + isMonkeyKingRunning); 264 if (isAgingTest && (isMonkeyRunning || isMonkeyKingRunning) 265 && !PowerManager.SHUTDOWN_LOW_BATTERY.equals(reason)) { 266 Slog.d(TAG, "AgingTestNotAllowShutdown"); 267 return true; 268 } 269 if (isAgingTest && "silent".equals(reason)) { 270 Slog.d(TAG, "AgingTestNotAllowShutdown silence reboot"); 271 return true; 272 } 273 return false; 274 } 275 276 private static class CloseDialogReceiver extends BroadcastReceiver 277 implements DialogInterface.OnDismissListener { 278 private Context mContext; 279 public Dialog dialog; 280 281 CloseDialogReceiver(Context context) { 282 mContext = context; 283 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 284 context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED); 285 } 286 287 @Override 288 public void onReceive(Context context, Intent intent) { 289 dialog.cancel(); 290 } 291 292 public void onDismiss(DialogInterface unused) { 293 mContext.unregisterReceiver(this); 294 } 295 } 296 297 /** 298 * Request a clean shutdown, waiting for subsystems to clean up their 299 * state etc. Must be called from a Looper thread in which its UI 300 * is shown. 301 * 302 * @param context Context used to display the shutdown progress dialog. This must be a context 303 * suitable for displaying UI (aka Themable). 304 * @param reason code to pass to the kernel (e.g. "recovery"), or null. 305 * @param confirm true if user confirmation is needed before shutting down. 306 */ 307 public static void reboot(final Context context, String reason, boolean confirm) { 308 mReboot = true; 309 mRebootSafeMode = false; 310 mRebootHasProgressBar = false; 311 mReason = reason; 312 shutdownInner(context, confirm); 313 } 314 315 /** 316 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI 317 * is shown. 318 * 319 * @param context Context used to display the shutdown progress dialog. This must be a context 320 * suitable for displaying UI (aka Themable). 321 * @param confirm true if user confirmation is needed before shutting down. 322 */ 323 public static void rebootSafeMode(final Context context, boolean confirm) { 324 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 325 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { 326 return; 327 } 328 329 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC 330 if (!isDeviceProvisioned(context) && OplusDLCUtils.isDLCVersion()) { 331 return; 332 } 333 // endif 334 335 mReboot = true; 336 mRebootSafeMode = true; 337 mRebootHasProgressBar = false; 338 mReason = null; 339 shutdownInner(context, confirm); 340 } 341 342 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC 343 private static boolean isDeviceProvisioned(Context context) { 344 return android.provider.Settings.Global.getInt(context.getContentResolver(), 345 android.provider.Settings.Global.DEVICE_PROVISIONED, 0) != 0; 346 } 347 // endif 348 349 private static ProgressDialog showShutdownDialog(Context context) { 350 // Throw up a system dialog to indicate the device is rebooting / shutting down. 351 ProgressDialog pd = new ProgressDialog(context); 352 353 // Path 1: Reboot to recovery for update 354 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE 355 // 356 // Path 1a: uncrypt needed 357 // Condition: if /cache/recovery/uncrypt_file exists but 358 // /cache/recovery/block.map doesn't. 359 // UI: determinate progress bar (mRebootHasProgressBar == True) 360 // 361 // * Path 1a is expected to be removed once the GmsCore shipped on 362 // device always calls uncrypt prior to reboot. 363 // 364 // Path 1b: uncrypt already done 365 // UI: spinning circle only (no progress bar) 366 // 367 // Path 2: Reboot to recovery for factory reset 368 // Condition: mReason == REBOOT_RECOVERY 369 // UI: spinning circle only (no progress bar) 370 // 371 // Path 3: Regular reboot / shutdown 372 // Condition: Otherwise 373 // UI: spinning circle only (no progress bar) 374 375 // mReason could be "recovery-update" or "recovery-update,quiescent". 376 if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { 377 // We need the progress bar if uncrypt will be invoked during the 378 // reboot, which might be time-consuming. 379 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() 380 && !(RecoverySystem.BLOCK_MAP_FILE.exists()); 381 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); 382 if (mRebootHasProgressBar) { 383 pd.setMax(100); 384 pd.setProgress(0); 385 pd.setIndeterminate(false); 386 boolean showPercent = context.getResources().getBoolean( 387 com.android.internal.R.bool.config_showPercentageTextDuringRebootToUpdate); 388 if (!showPercent) { 389 pd.setProgressPercentFormat(null); 390 } 391 pd.setProgressNumberFormat(null); 392 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 393 pd.setMessage(context.getText( 394 com.android.internal.R.string.reboot_to_update_prepare)); 395 } else { 396 if (showSysuiReboot()) { 397 return null; 398 } 399 pd.setIndeterminate(true); 400 pd.setMessage(context.getText( 401 com.android.internal.R.string.reboot_to_update_reboot)); 402 } 403 } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) { 404 if (RescueParty.isRecoveryTriggeredReboot()) { 405 // We're not actually doing a factory reset yet; we're rebooting 406 // to ask the user if they'd like to reset, so give them a less 407 // scary dialog message. 408 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 409 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 410 pd.setIndeterminate(true); 411 } else if (showSysuiReboot()) { 412 return null; 413 } else { 414 // Factory reset path. Set the dialog message accordingly. 415 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); 416 pd.setMessage(context.getText( 417 com.android.internal.R.string.reboot_to_reset_message)); 418 pd.setIndeterminate(true); 419 } 420 } else { 421 if (showSysuiReboot()) { 422 return null; 423 } 424 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 425 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 426 pd.setIndeterminate(true); 427 } 428 pd.setCancelable(false); 429 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 430 if(isPreVersion()){ 431 pd.show(); 432 } 433 return pd; 434 } 435 436 private static void showShutdownAnimation() { 437 Log.w(TAG, "showShutdownAnimation"); 438 SystemProperties.set("service.bootanim.exit", "0"); 439 SystemProperties.set("ctl.start", "banim_shut"); 440 } 441 442 private static boolean isPreVersion(){ 443 return "true".equals(SystemProperties.get("ro.build.oplus.pre_version")); 444 } 445 446 private static void delayForPlayAnimation() { 447 Log.i(TAG,"set service.shutanim.running to 1"); 448 if(beginAnimationTime <= 0){ 449 return; 450 } 451 endAnimationTime = beginAnimationTime-SystemClock.elapsedRealtime(); 452 if(endAnimationTime > 0){ 453 try { 454 Log.w(TAG, "sleep delayForPlayAnimation"); 455 Thread.currentThread().sleep(1500); 456 } catch (InterruptedException e) { 457 Log.e(TAG, "Shutdown stop bootanimation Thread.currentThread().sleep exception!"); 458 } 459 } 460 } 461 462 private static boolean showSysuiReboot() { 463 if (DEBUG) { 464 Log.d(TAG, "Attempting to use SysUI shutdown UI"); 465 } 466 try { 467 StatusBarManagerInternal service = LocalServices.getService( 468 StatusBarManagerInternal.class); 469 if (service.showShutdownUi(mReboot, mReason)) { 470 // Sysui will handle shutdown UI. 471 if (DEBUG) { 472 Log.d(TAG, "SysUI handling shutdown UI"); 473 } 474 return true; 475 } 476 } catch (Exception e) { 477 // If anything went wrong, ignore it and use fallback ui 478 } 479 if (DEBUG) { 480 Log.d(TAG, "SysUI is unavailable"); 481 } 482 return false; 483 } 484 485 private static void beginShutdownSequence(Context context) { 486 synchronized (sIsStartedGuard) { 487 if (sIsStarted) { 488 if (DEBUG) { 489 Log.d(TAG, "Shutdown sequence already running, returning."); 490 } 491 return; 492 } 493 sIsStarted = true; 494 } 495 if(isPreVersion()){ 496 sInstance.mProgressDialog = showShutdownDialog(context); 497 } else { 498 beginAnimationTime = SystemClock.elapsedRealtime()+MIN_SHUTDOWN_ANIMATION_PLAY_TIME; 499 showShutdownAnimation(); 500 } 501 502 sInstance.mContext = context; 503 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 504 505 // make sure we never fall asleep again 506 sInstance.mCpuWakeLock = null; 507 try { 508 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 509 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 510 sInstance.mCpuWakeLock.setReferenceCounted(false); 511 sInstance.mCpuWakeLock.acquire(); 512 } catch (SecurityException e) { 513 Log.w(TAG, "No permission to acquire wake lock", e); 514 sInstance.mCpuWakeLock = null; 515 } 516 517 // also make sure the screen stays on for better user experience 518 sInstance.mScreenWakeLock = null; 519 if (sInstance.mPowerManager.isScreenOn()) { 520 try { 521 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 522 PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 523 sInstance.mScreenWakeLock.setReferenceCounted(false); 524 sInstance.mScreenWakeLock.acquire(); 525 } catch (SecurityException e) { 526 Log.w(TAG, "No permission to acquire wake lock", e); 527 sInstance.mScreenWakeLock = null; 528 } 529 } 530 531 if (SecurityLog.isLoggingEnabled()) { 532 SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); 533 } 534 delayForPlayAnimation(); 535 536 // start the thread that initiates shutdown 537 sInstance.mHandler = new Handler() { 538 }; 539 sInstance.start(); 540 } 541 542 void actionDone() { 543 synchronized (mActionDoneSync) { 544 mActionDone = true; 545 mActionDoneSync.notifyAll(); 546 } 547 } 548 549 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT 550 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 551 /** 552 * @return void doShutdownDetect 553 */ 554 private static void doShutdownDetect(String count) { 555 File procFile = null; 556 FileWriter shutdown_detect = null; 557 try { 558 Log.e(TAG, "doShutdownDetect " + count); 559 procFile = new File("/proc/shutdown_detect"); 560 shutdown_detect = new FileWriter(procFile); 561 shutdown_detect.write(count); 562 } catch (java.io.IOException e) { 563 Log.w(TAG, "Failed to write to /proc/shutdown_detect", e); 564 } finally { 565 IoUtils.closeQuietly(shutdown_detect); 566 } 567 } 568 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */ 569 570 /** 571 * Makes sure we handle the shutdown gracefully. 572 * Shuts off power regardless of radio state if the allotted time has passed. 573 */ 574 public void run() { 575 TimingsTraceLog shutdownTimingLog = newTimingsLog(); 576 shutdownTimingLog.traceBegin("SystemServerShutdown"); 577 metricShutdownStart(); 578 metricStarted(METRIC_SYSTEM_SERVER); 579 580 //#ifdef OPLUS_EXTENSION_HOOK 581 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 582 doShutdownDetect("40"); 583 //#endif /* OPLUS_EXTENSION_HOOK */ 584 585 // Notify SurfaceFlinger that the device is shutting down. 586 // Transaction traces should be captured at this stage. 587 SurfaceControl.notifyShutdown(); 588 589 // Start dumping check points for this shutdown in a separate thread. 590 Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread( 591 new File(CHECK_POINTS_FILE_BASENAME)); 592 dumpCheckPointsThread.start(); 593 594 /* 595 * Write a system property in case the system_server reboots before we 596 * get to the actual hardware restart. If that happens, we'll retry at 597 * the beginning of the SystemServer startup. 598 */ 599 { 600 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 601 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 602 } 603 604 /* 605 * If we are rebooting into safe mode, write a system property 606 * indicating so. 607 */ 608 if (mRebootSafeMode) { 609 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 610 } 611 612 shutdownTimingLog.traceBegin("DumpPreRebootInfo"); 613 try { 614 Slog.i(TAG, "Logging pre-reboot information..."); 615 PreRebootLogger.log(mContext); 616 } catch (Exception e) { 617 Slog.e(TAG, "Failed to log pre-reboot information", e); 618 } 619 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo 620 621 metricStarted(METRIC_SEND_BROADCAST); 622 shutdownTimingLog.traceBegin("SendShutdownBroadcast"); 623 Log.i(TAG, "Sending shutdown broadcast..."); 624 625 // First send the high-level shut down broadcast. 626 mActionDone = false; 627 Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 628 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 629 final Bundle opts = BroadcastOptions.makeBasic() 630 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) 631 .toBundle(); 632 final ActivityManagerInternal activityManagerInternal = LocalServices.getService( 633 ActivityManagerInternal.class); 634 activityManagerInternal.broadcastIntentWithCallback(intent, 635 new IIntentReceiver.Stub() { 636 @Override 637 public void performReceive(Intent intent, int resultCode, String data, 638 Bundle extras, boolean ordered, boolean sticky, int sendingUser) { 639 mHandler.post(ShutdownThread.this::actionDone); 640 } 641 }, null, UserHandle.USER_ALL, null, null, opts); 642 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 643 synchronized (mActionDoneSync) { 644 while (!mActionDone) { 645 long delay = endTime - SystemClock.elapsedRealtime(); 646 if (delay <= 0) { 647 Log.w(TAG, "Shutdown broadcast timed out"); 648 //#ifdef OPLUS_EXTENSION_HOOK 649 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 650 doShutdownDetect("47"); 651 //#endif /* OPLUS_EXTENSION_HOOK */ 652 break; 653 } else if (mRebootHasProgressBar) { 654 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * 655 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); 656 sInstance.setRebootProgress(status, null); 657 } 658 try { 659 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); 660 } catch (InterruptedException e) { 661 } 662 } 663 } 664 if (mRebootHasProgressBar) { 665 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 666 } 667 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast 668 metricEnded(METRIC_SEND_BROADCAST); 669 670 Log.i(TAG, "Shutting down activity manager..."); 671 shutdownTimingLog.traceBegin("ShutdownActivityManager"); 672 metricStarted(METRIC_AM); 673 //#ifdef OPLUS_EXTENSION_HOOK 674 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 675 long startTime = SystemClock.elapsedRealtime(); 676 //#endif /* OPLUS_EXTENSION_HOOK */ 677 678 final IActivityManager am = 679 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 680 if (am != null) { 681 try { 682 am.shutdown(MAX_BROADCAST_TIME); 683 } catch (RemoteException e) { 684 } 685 } 686 if (mRebootHasProgressBar) { 687 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 688 } 689 //#ifdef OPLUS_EXTENSION_HOOK 690 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 691 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) { 692 doShutdownDetect("46"); 693 } 694 //#endif /* OPLUS_EXTENSION_HOOK */ 695 696 shutdownTimingLog.traceEnd();// ShutdownActivityManager 697 metricEnded(METRIC_AM); 698 699 Log.i(TAG, "Shutting down package manager..."); 700 shutdownTimingLog.traceBegin("ShutdownPackageManager"); 701 metricStarted(METRIC_PM); 702 //#ifdef OPLUS_EXTENSION_HOOK 703 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 704 startTime = SystemClock.elapsedRealtime(); 705 //#endif /* OPLUS_EXTENSION_HOOK */ 706 707 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); 708 if (pm != null) { 709 pm.shutdown(); 710 } 711 if (mRebootHasProgressBar) { 712 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 713 } 714 //#ifdef OPLUS_EXTENSION_HOOK 715 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 716 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) { 717 doShutdownDetect("45"); 718 } 719 //#endif /* OPLUS_EXTENSION_HOOK */ 720 721 shutdownTimingLog.traceEnd(); // ShutdownPackageManager 722 metricEnded(METRIC_PM); 723 724 // Shutdown radios. 725 shutdownTimingLog.traceBegin("ShutdownRadios"); 726 metricStarted(METRIC_RADIOS); 727 shutdownRadios(MAX_RADIO_WAIT_TIME); 728 if (mRebootHasProgressBar) { 729 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 730 } 731 shutdownTimingLog.traceEnd(); // ShutdownRadios 732 metricEnded(METRIC_RADIOS); 733 734 if (mRebootHasProgressBar) { 735 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 736 737 // If it's to reboot to install an update and uncrypt hasn't been 738 // done yet, trigger it now. 739 uncrypt(); 740 } 741 742 // Wait for the check points dump thread to finish, or kill it if not finished in time. 743 shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait"); 744 try { 745 dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME); 746 } catch (InterruptedException ex) { 747 } 748 Log.i(TAG, "run() shutdownAnimationService"); 749 750 shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait 751 752 shutdownTimingLog.traceEnd(); // SystemServerShutdown 753 metricEnded(METRIC_SYSTEM_SERVER); 754 saveMetrics(mReboot, mReason); 755 //#ifdef OPLUS_EXTENSION_HOOK 756 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 757 //Shutdown from Systemserver, set timeout 120s, 4*30. 758 doShutdownDetect("4"); 759 //#endif /* OPLUS_EXTENSION_HOOK */ 760 // Remaining work will be done by init, including vold shutdown 761 rebootOrShutdown(mContext, mReboot, mReason); 762 } 763 764 private static TimingsTraceLog newTimingsLog() { 765 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); 766 } 767 768 private static void metricStarted(String metricKey) { 769 synchronized (TRON_METRICS) { 770 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); 771 } 772 } 773 774 private static void metricEnded(String metricKey) { 775 synchronized (TRON_METRICS) { 776 TRON_METRICS 777 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); 778 } 779 } 780 781 private static void metricShutdownStart() { 782 synchronized (TRON_METRICS) { 783 TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis()); 784 } 785 } 786 787 private void setRebootProgress(final int progress, final CharSequence message) { 788 mHandler.post(new Runnable() { 789 @Override 790 public void run() { 791 if (mProgressDialog != null) { 792 mProgressDialog.setProgress(progress); 793 if (message != null) { 794 mProgressDialog.setMessage(message); 795 } 796 } 797 } 798 }); 799 } 800 801 private void shutdownRadios(final int timeout) { 802 // If a radio is wedged, disabling it may hang so we do this work in another thread, 803 // just in case. 804 final long endTime = SystemClock.elapsedRealtime() + timeout; 805 final boolean[] done = new boolean[1]; 806 Thread t = new Thread() { 807 public void run() { 808 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); 809 boolean radioOff; 810 811 TelephonyManager telephonyManager = mContext.getSystemService( 812 TelephonyManager.class); 813 814 radioOff = telephonyManager == null 815 || !telephonyManager.isAnyRadioPoweredOn(); 816 if (!radioOff) { 817 Log.w(TAG, "Turning off cellular radios..."); 818 metricStarted(METRIC_RADIO); 819 telephonyManager.shutdownAllRadios(); 820 } 821 822 Log.i(TAG, "Waiting for Radio..."); 823 824 long delay = endTime - SystemClock.elapsedRealtime(); 825 while (delay > 0) { 826 if (mRebootHasProgressBar) { 827 int status = (int)((timeout - delay) * 1.0 * 828 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout); 829 status += PACKAGE_MANAGER_STOP_PERCENT; 830 sInstance.setRebootProgress(status, null); 831 } 832 833 if (!radioOff) { 834 radioOff = !telephonyManager.isAnyRadioPoweredOn(); 835 if (radioOff) { 836 Log.i(TAG, "Radio turned off."); 837 metricEnded(METRIC_RADIO); 838 shutdownTimingsTraceLog 839 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); 840 } 841 } 842 843 if (radioOff) { 844 Log.i(TAG, "Radio shutdown complete."); 845 done[0] = true; 846 break; 847 } 848 SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS); 849 delay = endTime - SystemClock.elapsedRealtime(); 850 } 851 } 852 }; 853 854 t.start(); 855 try { 856 t.join(timeout); 857 } catch (InterruptedException ex) { 858 } 859 if (!done[0]) { 860 Log.w(TAG, "Timed out waiting for Radio shutdown."); 861 } 862 } 863 864 /** 865 * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 866 * or {@link #shutdown(Context, String, boolean)} instead. 867 * 868 * @param context Context used to vibrate or null without vibration 869 * @param reboot true to reboot or false to shutdown 870 * @param reason reason for reboot/shutdown 871 */ 872 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { 873 String subsysProp; 874 subsysProp = SystemProperties.get("vendor.peripheral.shutdown_critical_list", 875 "ERROR"); 876 //If we don't have the shutdown critical subsystem list we can't 877 //really do anything. Proceed with full system shutdown. 878 if (!subsysProp.equals("ERROR")) { 879 Log.i(TAG, "Shutdown critical subsyslist is :"+subsysProp+": "); 880 Log.i(TAG, "Waiting for a maximum of " + 881 (VENDOR_SUBSYS_MAX_WAIT_MS) + "ms"); 882 String[] subsysList = subsysProp.split(" "); 883 int wait_count = 0; 884 boolean okToShutdown = true; 885 String subsysState; 886 int subsysListLength = subsysList.length; 887 do { 888 okToShutdown = true; 889 for (int i = 0; i < subsysListLength; i++) { 890 subsysState = 891 SystemProperties.get( 892 "vendor.peripheral." + 893 subsysList[i] + 894 ".state", 895 "ERROR"); 896 if(subsysState.equals("ONLINE")) { 897 //We only want to delay shutdown while 898 //one of the shutdown critical 899 //subsystems still shows as 'ONLINE'. 900 okToShutdown = false; 901 } 902 } 903 if (okToShutdown == false) { 904 SystemClock.sleep(VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS); 905 wait_count++; 906 } 907 } while (okToShutdown != true && 908 wait_count < (VENDOR_SUBSYS_MAX_WAIT_MS/VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS)); 909 if (okToShutdown != true) { 910 for (int i = 0; i < subsysList.length; i++) { 911 subsysState = 912 SystemProperties.get( 913 "vendor.peripheral." + 914 subsysList[i] + 915 ".state", 916 "ERROR"); 917 if(subsysState.equals("ONLINE")) { 918 Log.w(TAG, "Subsystem " + subsysList[i]+ 919 "did not shut down within timeout"); 920 } 921 } 922 //#ifdef OPLUS_EXTENSION_HOOK 923 //koulongfei@ODM_WT.AD.Framework, 2023/08/22, Add for shutdown detect. 924 doShutdownDetect("43"); 925 //#endif /* OPLUS_EXTENSION_HOOK */ 926 } else { 927 Log.i(TAG, "Vendor subsystem(s) shutdown successful"); 928 } 929 } 930 if (sInstance.mPowerManager != null){ 931 Log.i(TAG, "mPowerManager.goToSleep"); 932 sInstance.mPowerManager.goToSleep(SystemClock.uptimeMillis()); 933 } 934 if (reboot) { 935 Log.i(TAG, "Rebooting, reason: " + reason); 936 PowerManagerService.lowLevelReboot(reason); 937 Log.e(TAG, "Reboot failed, will attempt shutdown instead"); 938 reason = null; 939 } else if (context != null) { 940 // vibrate before shutting down 941 try { 942 sInstance.playShutdownVibration(context); 943 } catch (Exception e) { 944 // Failure to vibrate shouldn't interrupt shutdown. Just log it. 945 Log.w(TAG, "Failed to vibrate during shutdown.", e); 946 } 947 948 } 949 // Shutdown power 950 Log.i(TAG, "Performing low-level shutdown..."); 951 PowerManagerService.lowLevelShutdown(reason); 952 } 953 954 /** 955 * Plays a vibration for shutdown. Along with playing a shutdown vibration, this method also 956 * sleeps the current Thread for some time, to allow the vibration to finish before the device 957 * shuts down. 958 */ 959 @VisibleForTesting // For testing vibrations without shutting down device 960 void playShutdownVibration(Context context) { 961 Vibrator vibrator = mInjector.getVibrator(context); 962 if (!vibrator.hasVibrator()) { 963 return; 964 } 965 966 VibrationEffect vibrationEffect = getValidShutdownVibration(context, vibrator); 967 vibrator.vibrate( 968 vibrationEffect, 969 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH)); 970 971 // vibrator is asynchronous so we have to wait to avoid shutting down too soon. 972 long vibrationDuration = vibrationEffect.getDuration(); 973 // A negative vibration duration may indicate a vibration effect whose duration is not 974 // known by the system (e.g. pre-baked effects). In that case, use the default shutdown 975 // vibration duration. 976 mInjector.sleep(vibrationDuration < 0 ? DEFAULT_SHUTDOWN_VIBRATE_MS : vibrationDuration); 977 } 978 979 private static void saveMetrics(boolean reboot, String reason) { 980 StringBuilder metricValue = new StringBuilder(); 981 metricValue.append("reboot:"); 982 metricValue.append(reboot ? "y" : "n"); 983 metricValue.append(",").append("reason:").append(reason); 984 final int metricsSize = TRON_METRICS.size(); 985 for (int i = 0; i < metricsSize; i++) { 986 final String name = TRON_METRICS.keyAt(i); 987 final long value = TRON_METRICS.valueAt(i); 988 if (value < 0) { 989 Log.e(TAG, "metricEnded wasn't called for " + name); 990 continue; 991 } 992 metricValue.append(',').append(name).append(':').append(value); 993 } 994 File tmp = new File(METRICS_FILE_BASENAME + ".tmp"); 995 boolean saved = false; 996 try (FileOutputStream fos = new FileOutputStream(tmp)) { 997 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8)); 998 saved = true; 999 } catch (IOException e) { 1000 Log.e(TAG,"Cannot save shutdown metrics", e); 1001 } 1002 if (saved) { 1003 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); 1004 } 1005 } 1006 1007 private void uncrypt() { 1008 Log.i(TAG, "Calling uncrypt and monitoring the progress..."); 1009 1010 final RecoverySystem.ProgressListener progressListener = 1011 new RecoverySystem.ProgressListener() { 1012 @Override 1013 public void onProgress(int status) { 1014 if (status >= 0 && status < 100) { 1015 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100). 1016 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100); 1017 status += MOUNT_SERVICE_STOP_PERCENT; 1018 CharSequence msg = mContext.getText( 1019 com.android.internal.R.string.reboot_to_update_package); 1020 sInstance.setRebootProgress(status, msg); 1021 } else if (status == 100) { 1022 CharSequence msg = mContext.getText( 1023 com.android.internal.R.string.reboot_to_update_reboot); 1024 sInstance.setRebootProgress(status, msg); 1025 } else { 1026 // Ignored 1027 } 1028 } 1029 }; 1030 1031 final boolean[] done = new boolean[1]; 1032 done[0] = false; 1033 Thread t = new Thread() { 1034 @Override 1035 public void run() { 1036 RecoverySystem rs = (RecoverySystem) mContext.getSystemService( 1037 Context.RECOVERY_SERVICE); 1038 String filename = null; 1039 try { 1040 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null); 1041 rs.processPackage(mContext, new File(filename), progressListener); 1042 } catch (IOException e) { 1043 Log.e(TAG, "Error uncrypting file", e); 1044 } 1045 done[0] = true; 1046 } 1047 }; 1048 t.start(); 1049 1050 try { 1051 t.join(MAX_UNCRYPT_WAIT_TIME); 1052 } catch (InterruptedException unused) { 1053 } 1054 if (!done[0]) { 1055 Log.w(TAG, "Timed out waiting for uncrypt."); 1056 final int uncryptTimeoutError = 100; 1057 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", 1058 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); 1059 try { 1060 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); 1061 } catch (IOException e) { 1062 Log.e(TAG, "Failed to write timeout message to uncrypt status", e); 1063 } 1064 } 1065 } 1066 1067 /** 1068 * Provides a {@link VibrationEffect} to be used for shutdown. 1069 * 1070 * <p>The vibration to be played is derived from the shutdown vibration file (which the device 1071 * should specify at `com.android.internal.R.string.config_defaultShutdownVibrationFile`). A 1072 * fallback vibration maybe used in one of these conditions: 1073 * <ul> 1074 * <li>A vibration file has not been specified, or if the specified file does not exist 1075 * <li>If the content of the file does not represent a valid serialization of a 1076 * {@link VibrationEffect} 1077 * <li>If the {@link VibrationEffect} specified in the file is not suitable for 1078 * a shutdown vibration (such as indefinite vibrations) 1079 * </ul> 1080 */ 1081 private VibrationEffect getValidShutdownVibration(Context context, Vibrator vibrator) { 1082 VibrationEffect parsedEffect = parseVibrationEffectFromFile( 1083 mInjector.getDefaultShutdownVibrationEffectFilePath(context), 1084 vibrator); 1085 1086 if (parsedEffect == null) { 1087 return createDefaultVibrationEffect(); 1088 } 1089 1090 long parsedEffectDuration = parsedEffect.getDuration(); 1091 if (parsedEffectDuration == Long.MAX_VALUE) { 1092 // This means that the effect does not have a defined end. 1093 // Since we don't want to vibrate forever while trying to shutdown, we ignore this 1094 // parsed effect and use the default one instead. 1095 Log.w(TAG, "The parsed shutdown vibration is indefinite."); 1096 return createDefaultVibrationEffect(); 1097 } 1098 1099 return parsedEffect; 1100 } 1101 1102 private static VibrationEffect parseVibrationEffectFromFile( 1103 String filePath, Vibrator vibrator) { 1104 if (!TextUtils.isEmpty(filePath)) { 1105 try { 1106 return VibrationXmlParser.parseDocument(new FileReader(filePath)).resolve(vibrator); 1107 } catch (Exception e) { 1108 Log.e(TAG, "Error parsing default shutdown vibration effect.", e); 1109 } 1110 } 1111 return null; 1112 } 1113 1114 private static VibrationEffect createDefaultVibrationEffect() { 1115 return VibrationEffect.createOneShot( 1116 DEFAULT_SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE); 1117 } 1118 1119 /** Utility class to inject instances, for easy testing. */ 1120 @VisibleForTesting 1121 static class Injector { 1122 public Vibrator getVibrator(Context context) { 1123 return new SystemVibrator(context); 1124 } 1125 1126 public void sleep(long durationMs) { 1127 try { 1128 Thread.sleep(durationMs); 1129 } catch (InterruptedException unused) { 1130 // this is not critical and does not require logging. 1131 } 1132 } 1133 1134 public String getDefaultShutdownVibrationEffectFilePath(Context context) { 1135 return context.getResources().getString( 1136 com.android.internal.R.string.config_defaultShutdownVibrationFile); 1137 } 1138 } 1139 }
最新发布
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值