1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.devicepolicy; 18 19 import android.annotation.Nullable; 20 import android.app.admin.DevicePolicyEventLogger; 21 import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback; 22 import android.app.admin.StartInstallingUpdateCallback; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.BatteryManager; 27 import android.os.Environment; 28 import android.os.FileUtils; 29 import android.os.ParcelFileDescriptor; 30 import android.os.PowerManager; 31 import android.os.Process; 32 import android.os.RemoteException; 33 import android.stats.devicepolicy.DevicePolicyEnums; 34 import android.util.Log; 35 36 import java.io.File; 37 import java.io.FileOutputStream; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.io.OutputStream; 41 42 abstract class UpdateInstaller { 43 private StartInstallingUpdateCallback mCallback; 44 private ParcelFileDescriptor mUpdateFileDescriptor; 45 private DevicePolicyConstants mConstants; 46 protected Context mContext; 47 48 @Nullable protected File mCopiedUpdateFile; 49 50 static final String TAG = "UpdateInstaller"; 51 private DevicePolicyManagerService.Injector mInjector; 52 UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector, DevicePolicyConstants constants)53 protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor, 54 StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector, 55 DevicePolicyConstants constants) { 56 mContext = context; 57 mCallback = callback; 58 mUpdateFileDescriptor = updateFileDescriptor; 59 mInjector = injector; 60 mConstants = constants; 61 } 62 installUpdateInThread()63 public abstract void installUpdateInThread(); 64 startInstallUpdate()65 public void startInstallUpdate() { 66 mCopiedUpdateFile = null; 67 if (!isBatteryLevelSufficient()) { 68 notifyCallbackOnError( 69 InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW, 70 "The battery level must be above " 71 + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or" 72 + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging"); 73 return; 74 } 75 Thread thread = new Thread(() -> { 76 mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir(); 77 if (mCopiedUpdateFile == null) { 78 notifyCallbackOnError( 79 InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN, 80 "Error while copying file."); 81 return; 82 } 83 installUpdateInThread(); 84 }); 85 thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND); 86 thread.start(); 87 } 88 isBatteryLevelSufficient()89 private boolean isBatteryLevelSufficient() { 90 Intent batteryStatus = mContext.registerReceiver( 91 /* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 92 float batteryPercentage = calculateBatteryPercentage(batteryStatus); 93 boolean isBatteryPluggedIn = 94 batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, /* defaultValue= */ -1) > 0; 95 return isBatteryPluggedIn 96 ? batteryPercentage >= mConstants.BATTERY_THRESHOLD_CHARGING 97 : batteryPercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING; 98 } 99 calculateBatteryPercentage(Intent batteryStatus)100 private float calculateBatteryPercentage(Intent batteryStatus) { 101 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, /* defaultValue= */ -1); 102 int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, /* defaultValue= */ -1); 103 return 100 * level / (float) scale; 104 } 105 copyUpdateFileToDataOtaPackageDir()106 private File copyUpdateFileToDataOtaPackageDir() { 107 try { 108 File destination = createNewFileWithPermissions(); 109 copyToFile(destination); 110 return destination; 111 } catch (IOException e) { 112 Log.w(TAG, "Failed to copy update file to OTA directory", e); 113 notifyCallbackOnError( 114 InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN, 115 Log.getStackTraceString(e)); 116 return null; 117 } 118 } 119 createNewFileWithPermissions()120 private File createNewFileWithPermissions() throws IOException { 121 File destination = File.createTempFile( 122 "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package")); 123 FileUtils.setPermissions( 124 /* path= */ destination, 125 /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH, 126 /* uid= */ -1, /* gid= */ -1); 127 return destination; 128 } 129 copyToFile(File destination)130 private void copyToFile(File destination) throws IOException { 131 try (OutputStream out = new FileOutputStream(destination); 132 InputStream in = new ParcelFileDescriptor.AutoCloseInputStream( 133 mUpdateFileDescriptor)) { 134 FileUtils.copy(in, out); 135 } 136 } 137 cleanupUpdateFile()138 void cleanupUpdateFile() { 139 if (mCopiedUpdateFile != null && mCopiedUpdateFile.exists()) { 140 mCopiedUpdateFile.delete(); 141 } 142 } 143 notifyCallbackOnError(int errorCode, String errorMessage)144 protected void notifyCallbackOnError(int errorCode, String errorMessage) { 145 cleanupUpdateFile(); 146 DevicePolicyEventLogger 147 .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE_ERROR) 148 .setInt(errorCode) 149 .write(); 150 try { 151 mCallback.onStartInstallingUpdateError(errorCode, errorMessage); 152 } catch (RemoteException e) { 153 Log.d(TAG, "Error while calling callback", e); 154 } 155 } 156 notifyCallbackOnSuccess()157 protected void notifyCallbackOnSuccess() { 158 cleanupUpdateFile(); 159 mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER); 160 } 161 } 162