summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Sauer <nicksauer@google.com>2018-02-09 21:58:42 -0800
committerNicholas Sauer <nicksauer@google.com>2018-02-12 20:42:19 -0800
commit2dc004693b725be42de5718f412ff3b9c24d2e9e (patch)
tree82eec9b94294da6941da8f7ed42ab747134d3f84
parentf44987591d963a6e15a6412ee158d2c75ef23001 (diff)
downloadSystemUpdater-2dc004693b725be42de5718f412ff3b9c24d2e9e.tar.gz
Update the SystemUpdater
- Use update engine to install updates - More car UX cleanup - Request permissions to access storage bug: 72832693 Test: make SystemUpdater; install and test update on device. Change-Id: Iebca00a9a8c9bff735b63c62d99f51975c2f9264
-rw-r--r--AndroidManifest.xml10
-rw-r--r--res/layout/folder_list.xml22
-rw-r--r--res/values/strings.xml10
-rw-r--r--res/values/styles.xml4
-rw-r--r--src/com/android/car/systemupdater/DeviceListFragment.java59
-rw-r--r--src/com/android/car/systemupdater/SystemUpdaterActivity.java33
-rw-r--r--src/com/android/car/systemupdater/UpdateLayoutFragment.java198
-rw-r--r--src/com/android/car/systemupdater/UpdateParser.java120
8 files changed, 295 insertions, 161 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fb2e14e..e03b9c7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -17,17 +17,11 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.car.systemupdater" >
- <uses-sdk android:minSdkVersion="21" />
+ package="com.android.car.systemupdater">
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
- <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.REBOOT" />
- <uses-permission android:name="android.permission.RECOVERY" />
- <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-feature android:name="android.hardware.usb.host" />
<application
diff --git a/res/layout/folder_list.xml b/res/layout/folder_list.xml
index ba3eeff..33982d6 100644
--- a/res/layout/folder_list.xml
+++ b/res/layout/folder_list.xml
@@ -14,12 +14,20 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/current_path"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/ListPath"
+ android:layout_marginStart="@dimen/car_gutter_size"
+ android:layout_marginEnd="@dimen/car_margin"/>
<androidx.car.widget.PagedListView
android:id="@+id/folder_list"
@@ -27,4 +35,4 @@
android:layout_height="match_parent"
app:showPagedListViewDivider="true"
app:gutter="start"/>
-</FrameLayout>
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0aa33b0..d72f65a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -40,10 +40,6 @@
<string name="verify_in_progress">Verifying update&#8230;</string>
<!-- An error message indicating that verification failed. [CHAR LIMIT=40] -->
<string name="verify_failure">Verification Failed. Please select a valid update file.</string>
- <!-- A status that indicates that the update is being copied before installation. [CHAR LIMIT=40] -->
- <string name="copy_in_progress">Copying to device&#8230;</string>
- <!-- An error message indicating that copying failed. [CHAR LIMIT=40] -->
- <string name="copy_failure">Failed to copy to the device. Check disk space and try again.</string>
<!-- A status that indicates that the update is ready to be installed. [CHAR LIMIT=40] -->
<string name="install_ready">The update is ready to be installed.</string>
<!-- A status that indicates the installation process is running. [CHAR LIMIT=40] -->
@@ -52,4 +48,10 @@
<string name="install_success">The update is successful.</string>
<!-- A status that indicates that installation failed. [CHAR LIMIT=40] -->
<string name="install_failed">System update installation failed.</string>
+ <!-- A status that indicates the system is about to reboot. [CHAR LIMIT=40] -->
+ <string name="rebooting">The update is successful. Rebooting now&#8230;</string>
+ <!-- The volumes found on the device. [CHAR LIMIT=40] -->
+ <string name="volumes">Volumes (%d)</string>
+ <!-- The path of the current directory. [CHAR LIMIT=20] -->
+ <string name="path">Path: %s</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c8e0f0b..e3a19e6 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -60,4 +60,8 @@
<item name="android:background">?android:selectableItemBackground</item>
<item name="android:gravity">center</item>
</style>
+
+ <style name="ListPath" parent="CarBody2">
+ <item name="android:textColor">@color/car_accent</item>
+ </style>
</resources>
diff --git a/src/com/android/car/systemupdater/DeviceListFragment.java b/src/com/android/car/systemupdater/DeviceListFragment.java
index a19e652..d32cb7e 100644
--- a/src/com/android/car/systemupdater/DeviceListFragment.java
+++ b/src/com/android/car/systemupdater/DeviceListFragment.java
@@ -25,31 +25,39 @@ import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import android.widget.Toast;
-import android.util.Log;
-
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemProvider;
-import androidx.car.widget.TextListItem;
import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.TextListItem;
+
/**
-* Display a list of files and directories.
-*/
+ * Display a list of files and directories.
+ */
public class DeviceListFragment extends Fragment {
private static final String TAG = "DeviceListFragment";
private static final String UPDATE_FILE_SUFFIX = ".zip";
+ private static final FileFilter UPDATE_FILE_FILTER =
+ file -> !file.isHidden() && (file.isDirectory()
+ || file.getName().toLowerCase().endsWith(UPDATE_FILE_SUFFIX));
+
private final Stack<File> mFileStack = new Stack<>();
private StorageManager mStorageManager;
@@ -57,6 +65,7 @@ public class DeviceListFragment extends Fragment {
private List<File> mListItems;
private ListItemAdapter mAdapter;
private FileItemProvider mItemProvider;
+ private TextView mCurrentPathView;
private final StorageEventListener mListener = new StorageEventListener() {
@Override
@@ -91,21 +100,22 @@ public class DeviceListFragment extends Fragment {
Toast.makeText(context, R.string.cannot_access_storage, Toast.LENGTH_LONG).show();
return;
}
- showMountedVolumes();
}
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
mAdapter = new ListItemAdapter(getContext(), mItemProvider);
return inflater.inflate(R.layout.folder_list, container, false);
- }
+ }
- @Override
+ @Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
PagedListView folderListView = (PagedListView) view.findViewById(R.id.folder_list);
folderListView.setMaxPages(PagedListView.ItemCap.UNLIMITED);
folderListView.setAdapter(mAdapter);
+
+ mCurrentPathView = (TextView) view.findViewById(R.id.current_path);
}
@Override
@@ -118,6 +128,8 @@ public class DeviceListFragment extends Fragment {
actionBar.setDisplayShowTitleEnabled(false);
activity.findViewById(R.id.action_bar_icon_container)
.setOnClickListener(v -> onBackPressed());
+
+ showMountedVolumes();
}
@Override
@@ -145,10 +157,15 @@ public class DeviceListFragment extends Fragment {
ArrayList<File> volumes = new ArrayList<>(vols.size());
for (VolumeInfo vol : vols) {
File path = vol.getPathForUser(getActivity().getUserId());
- if (vol.getState() == VolumeInfo.STATE_MOUNTED && path != null) {
+ if (vol.getState() == VolumeInfo.STATE_MOUNTED
+ && vol.getType() == VolumeInfo.TYPE_PUBLIC
+ && path != null) {
volumes.add(path);
}
}
+
+ // Otherwise show all of the available volumes.
+ mCurrentPathView.setText(getString(R.string.volumes, volumes.size()));
setFileList(volumes);
}
@@ -198,11 +215,13 @@ public class DeviceListFragment extends Fragment {
return;
}
+ mCurrentPathView.setText(getString(R.string.path, folder.getAbsolutePath()));
+
// Retrieve the list of files and update the displayed list.
new AsyncTask<File, Void, File[]>() {
@Override
protected File[] doInBackground(File... file) {
- return file[0].listFiles();
+ return file[0].listFiles(UPDATE_FILE_FILTER);
}
@Override
@@ -210,6 +229,8 @@ public class DeviceListFragment extends Fragment {
super.onPostExecute(results);
if (results == null) {
results = new File[0];
+ Toast.makeText(getContext(), R.string.cannot_access_storage,
+ Toast.LENGTH_LONG).show();
}
setFileList(Arrays.asList(results));
}
@@ -232,7 +253,7 @@ public class DeviceListFragment extends Fragment {
TextListItem item = new TextListItem(mContext);
File file = mListItems.get(position);
if (file != null) {
- item.setTitle(file.getAbsolutePath());
+ item.setTitle(file.getName());
item.setOnClickListener(v -> onFileSelected(file));
} else {
item.setTitle(getString(R.string.unknown_file));
@@ -242,7 +263,7 @@ public class DeviceListFragment extends Fragment {
@Override
public int size() {
- return mListItems.size();
+ return mListItems == null ? 0 : mListItems.size();
}
}
diff --git a/src/com/android/car/systemupdater/SystemUpdaterActivity.java b/src/com/android/car/systemupdater/SystemUpdaterActivity.java
index e5fd9fb..91cbe6b 100644
--- a/src/com/android/car/systemupdater/SystemUpdaterActivity.java
+++ b/src/com/android/car/systemupdater/SystemUpdaterActivity.java
@@ -15,7 +15,11 @@
*/
package com.android.car.systemupdater;
+import android.Manifest;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -27,9 +31,22 @@ import java.io.File;
public class SystemUpdaterActivity extends AppCompatActivity
implements DeviceListFragment.SystemUpdater {
+ private static final int STORAGE_PERMISSIONS_REQUEST_CODE = 0;
+ private static final String[] REQUIRED_STORAGE_PERMISSIONS = new String[]{
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, REQUIRED_STORAGE_PERMISSIONS,
+ STORAGE_PERMISSIONS_REQUEST_CODE);
+ }
+
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@@ -43,6 +60,22 @@ public class SystemUpdaterActivity extends AppCompatActivity
}
@Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (STORAGE_PERMISSIONS_REQUEST_CODE == requestCode) {
+ if (grantResults.length == 0) {
+ finish();
+ }
+ for (int grantResult : grantResults) {
+ if (grantResult != PackageManager.PERMISSION_GRANTED) {
+ finish();
+ }
+ }
+ }
+ }
+
+ @Override
public void applyUpdate(File file) {
UpdateLayoutFragment fragment = UpdateLayoutFragment.getInstance(file);
getSupportFragmentManager().beginTransaction()
diff --git a/src/com/android/car/systemupdater/UpdateLayoutFragment.java b/src/com/android/car/systemupdater/UpdateLayoutFragment.java
index 70d41a8..699ae19 100644
--- a/src/com/android/car/systemupdater/UpdateLayoutFragment.java
+++ b/src/com/android/car/systemupdater/UpdateLayoutFragment.java
@@ -15,9 +15,13 @@
*/
package com.android.car.systemupdater;
+import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.RecoverySystem;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
@@ -32,20 +36,17 @@ import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.internal.util.Preconditions;
+
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
/** Display update state and progress. */
public class UpdateLayoutFragment extends Fragment {
private static final String TAG = "UpdateLayoutFragment";
- private static final int COPY_BUF_SIZE = 0x10000; // 64k
private static final String EXTRA_UPDATE_FILE = "extra_update_file";
- private static final String UPDATE_FILE_NAME = "update.zip";
+ private static final int PERCENT_MAX = 100;
+ private static final String REBOOT_REASON = "reboot-ab-update";
private ProgressBar mProgressBar;
private TextView mContentTitle;
@@ -53,9 +54,11 @@ public class UpdateLayoutFragment extends Fragment {
private TextView mContentDetails;
private File mUpdateFile;
private Button mSystemUpdateToolbarAction;
- private PackageVerifier mPackageVerifier;
- private CopyFile mCopyFile;
- private InstallUpdate mInstallUpdate;
+ private PowerManager mPowerManager;
+ private final UpdateVerifier mPackageVerifier = new UpdateVerifier();
+ private final UpdateEngine mUpdateEngine = new UpdateEngine();
+
+ private final CarUpdateEngineCallback mCarUpdateEngineCallback = new CarUpdateEngineCallback();
/** Create a {@link DeviceListFragment}. */
public static UpdateLayoutFragment getInstance(File file) {
@@ -71,11 +74,12 @@ public class UpdateLayoutFragment extends Fragment {
super.onCreate(savedInstanceState);
mUpdateFile = new File(getArguments().getString(EXTRA_UPDATE_FILE));
+ mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ Bundle savedInstanceState) {
return inflater.inflate(R.layout.system_update_auto_content, container, false);
}
@@ -102,31 +106,28 @@ public class UpdateLayoutFragment extends Fragment {
mProgressBar = (ProgressBar) activity.findViewById(R.id.progress_bar);
mSystemUpdateToolbarAction = activity.findViewById(R.id.system_update_auto_toolbar_action);
+ mProgressBar.setIndeterminate(true);
+ mProgressBar.setVisibility(View.VISIBLE);
+ showStatus(R.string.verify_in_progress);
- mPackageVerifier = new PackageVerifier();
mPackageVerifier.execute(mUpdateFile);
}
@Override
public void onStop() {
super.onStop();
- if (mCopyFile != null) {
- mCopyFile.cancel(true);
- }
- if (mInstallUpdate != null) {
- mInstallUpdate.cancel(true);
- }
if (mPackageVerifier != null) {
mPackageVerifier.cancel(true);
}
}
+ /** Update the status information. */
private void showStatus(@StringRes int status) {
mContentTitle.setText(status);
}
/** Show the install now button. */
- private void showInstallNow(File update) {
+ private void showInstallNow(UpdateParser.ParsedUpdate update) {
mContentTitle.setText(R.string.install_ready);
mContentInfo.append(getString(R.string.update_file_name, mUpdateFile.getName()));
mContentInfo.append(System.getProperty("line.separator"));
@@ -138,141 +139,92 @@ public class UpdateLayoutFragment extends Fragment {
mSystemUpdateToolbarAction.setVisibility(View.VISIBLE);
}
- /** Attempt to install the update that is copied to the device. */
- private void installUpdate(File update) {
- mInstallUpdate = new InstallUpdate();
- mInstallUpdate.execute(update);
+ /** Reboot the system. */
+ private void rebootNow() {
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Log.i(TAG, "Rebooting Now.");
+ }
+ mPowerManager.reboot(REBOOT_REASON);
}
- /** Attempt to verify the package. */
- private class PackageVerifier extends AsyncTask<File, Void, File> {
+ /** Attempt to install the update that is copied to the device. */
+ private void installUpdate(UpdateParser.ParsedUpdate parsedUpdate) {
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setVisibility(View.VISIBLE);
+ mProgressBar.setMax(PERCENT_MAX);
+ mSystemUpdateToolbarAction.setVisibility(View.GONE);
+ showStatus(R.string.install_in_progress);
+
+ mUpdateEngine.bind(mCarUpdateEngineCallback,
+ new Handler(getContext().getMainLooper()));
+ mUpdateEngine.applyPayload(
+ parsedUpdate.mUrl, parsedUpdate.mOffset, parsedUpdate.mSize, parsedUpdate.mProps);
- @Override
- public void onPreExecute() {
- mProgressBar.setIndeterminate(true);
- mProgressBar.setVisibility(View.VISIBLE);
- showStatus(R.string.verify_in_progress);
- }
+ }
+
+ /** Attempt to verify the update and extract information needed for installation. */
+ private class UpdateVerifier extends AsyncTask<File, Void, UpdateParser.ParsedUpdate> {
@Override
- protected File doInBackground(File... files) {
+ protected UpdateParser.ParsedUpdate doInBackground(File... files) {
+ Preconditions.checkArgument(files.length > 0, "No file specified");
File file = files[0];
try {
- RecoverySystem.verifyPackage(file, null, null);
- return file;
- } catch (GeneralSecurityException | IOException e) {
- Log.e(TAG, String.format("While verifying package: %s", file), e);
+ return UpdateParser.parse(file);
+ } catch (IOException e) {
+ Log.e(TAG, String.format("For file %s", file), e);
return null;
}
}
@Override
- protected void onPostExecute(File result) {
+ protected void onPostExecute(UpdateParser.ParsedUpdate result) {
mProgressBar.setVisibility(View.GONE);
if (result == null) {
showStatus(R.string.verify_failure);
return;
}
-
- mCopyFile = new CopyFile();
- mCopyFile.execute(result);
- }
- }
-
- /** Copy the update file to the data partition so it can be installed. */
- private class CopyFile extends AsyncTask<File, Integer, File> {
- private final File mCacheDir;
-
- CopyFile() {
- mCacheDir = getContext().getCacheDir();
- }
-
- @Override
- public void onPreExecute() {
- showStatus(R.string.copy_in_progress);
- mProgressBar.setIndeterminate(false);
- mProgressBar.setVisibility(View.VISIBLE);
- mProgressBar.setMax((int)(mUpdateFile.length() / COPY_BUF_SIZE));
- }
-
- @Override
- protected File doInBackground(File... files) {
- final File file = files[0];
- if (mCacheDir.getFreeSpace() < file.length()) {
- Log.e(TAG, "Not enough cache space!");
- return null;
- }
- final File dest = new File(mCacheDir, UPDATE_FILE_NAME);
- try {
- copy(file, dest);
- return dest;
- } catch (IOException e) {
- Log.e(TAG, "Error when copying file to cache", e);
- dest.delete();
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(File result) {
- mProgressBar.setVisibility(View.GONE);
- if (result == null) {
- // Copy failed
- showStatus(R.string.copy_failure);
+ if (!result.isValid()) {
+ showStatus(R.string.verify_failure);
+ Log.e(TAG, String.format("Failed verification %s", result));
return;
}
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Log.i(TAG, result.toString());
+ }
showInstallNow(result);
}
-
- protected void onProgressUpdate(Integer... progress) {
- mProgressBar.incrementProgressBy(progress[0]);
- }
-
- /** Copy a file from {@code src} to {@code dest}. */
- private void copy(File src, File dst) throws IOException {
- try (InputStream in = new FileInputStream(src);
- OutputStream out = new FileOutputStream(dst)) {
- final byte[] buf = new byte[COPY_BUF_SIZE];
- int len;
- int count = 0;
- while ((len = in.read(buf)) > 0) {
- out.write(buf, 0, len);
- publishProgress(++count);
- }
- }
- }
}
- /** Attempt to install the package. */
- private class InstallUpdate extends AsyncTask<File, Void, Boolean> {
-
- @Override
- public void onPreExecute() {
- mProgressBar.setIndeterminate(true);
- mProgressBar.setVisibility(View.VISIBLE);
- mSystemUpdateToolbarAction.setVisibility(View.GONE);
- showStatus(R.string.install_in_progress);
- }
+ /** Handles events from the UpdateEngine. */
+ public class CarUpdateEngineCallback extends UpdateEngineCallback {
@Override
- protected Boolean doInBackground(File... files) {
- File file = files[0];
- try {
- RecoverySystem.installPackage(getContext(), file);
- return true;
- } catch (IOException e) {
- Log.e(TAG, "While installing the update package", e);
- return false;
+ public void onStatusUpdate(int status, float percent) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, String.format("onStatusUpdate %d, Percent %.2f", status, percent));
+ }
+ switch (status) {
+ case UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT:
+ rebootNow();
+ break;
+ case UpdateEngine.UpdateStatusConstants.DOWNLOADING:
+ mProgressBar.setProgress((int) (percent * 100));
+ break;
+ default:
+ // noop
}
}
@Override
- protected void onPostExecute(Boolean result) {
+ public void onPayloadApplicationComplete(int errorCode) {
+ Log.w(TAG, String.format("onPayloadApplicationComplete %d", errorCode));
+ showStatus(errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS
+ ? R.string.install_success
+ : R.string.install_failed);
mProgressBar.setVisibility(View.GONE);
mSystemUpdateToolbarAction.setVisibility(View.GONE);
-
- showStatus(result ? R.string.install_success : R.string.install_failed);
}
}
}
diff --git a/src/com/android/car/systemupdater/UpdateParser.java b/src/com/android/car/systemupdater/UpdateParser.java
new file mode 100644
index 0000000..d48096d
--- /dev/null
+++ b/src/com/android/car/systemupdater/UpdateParser.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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.car.systemupdater;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/** Parse an A/B update zip file. */
+class UpdateParser {
+
+ private static final String TAG = "UpdateLayoutFragment";
+ private static final String PAYLOAD_BIN_FILE = "payload.bin";
+ private static final String PAYLOAD_PROPERTIES = "payload_properties.txt";
+ private static final String FILE_URL_PREFIX = "file://";
+ private static final int ZIP_FILE_HEADER = 30;
+
+ private UpdateParser() {
+ }
+
+ /**
+ * Parse a zip file containing a system update and return a non null ParsedUpdate.
+ */
+ @Nullable
+ static ParsedUpdate parse(@NonNull File file) throws IOException {
+ Preconditions.checkNotNull(file);
+
+ long payloadOffset = 0;
+ long payloadSize = 0;
+ boolean payloadFound = false;
+ String[] props = null;
+
+ try (ZipFile zipFile = new ZipFile(file)) {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ long fileSize = entry.getCompressedSize();
+ if (!payloadFound) {
+ payloadOffset += ZIP_FILE_HEADER + entry.getName().length();
+ if (entry.getExtra() != null) {
+ payloadOffset += entry.getExtra().length;
+ }
+ }
+
+ if (entry.isDirectory()) {
+ continue;
+ } else if (entry.getName().equals(PAYLOAD_BIN_FILE)) {
+ payloadSize = fileSize;
+ payloadFound = true;
+ } else if (entry.getName().equals(PAYLOAD_PROPERTIES)) {
+ try (BufferedReader buffer = new BufferedReader(
+ new InputStreamReader(zipFile.getInputStream(entry)))) {
+ props = buffer.lines().toArray(String[]::new);
+ }
+ }
+ if (!payloadFound) {
+ payloadOffset += fileSize;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, String.format("Entry %s", entry.getName()));
+ }
+ }
+ }
+ return new ParsedUpdate(file, payloadOffset, payloadSize, props);
+ }
+
+ /** Information parsed from an update file. */
+ static class ParsedUpdate {
+ final String mUrl;
+ final long mOffset;
+ final long mSize;
+ final String[] mProps;
+
+ ParsedUpdate(File file, long offset, long size, String[] props) {
+ mUrl = FILE_URL_PREFIX + file.getAbsolutePath();
+ mOffset = offset;
+ mSize = size;
+ mProps = props;
+ }
+
+ /** Verify the update information is correct. */
+ boolean isValid() {
+ return mOffset >= 0 && mSize > 0 && mProps != null;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.getDefault(),
+ "ParsedUpdate: URL=%s, offset=%d, size=%s, props=%s",
+ mUrl, mOffset, mSize, Arrays.toString(mProps));
+ }
+ }
+}