summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRakesh Iyer <rni@google.com>2016-10-19 23:58:48 -0700
committerRakesh Iyer <rni@google.com>2016-10-20 00:27:48 -0700
commit3506f3f39a1328465009ec080a741373b338bead (patch)
tree0cd6d8131ed0e35fe560831390bfe828f400845f
parentd92757c877e943e3b01b5903567bea29796c31f5 (diff)
downloadSystemUpdater-3506f3f39a1328465009ec080a741373b338bead.tar.gz
Add car system updater.android-wear-8.1.0_r1android-wear-8.0.0_r1android-vts-8.1_r9android-vts-8.1_r8android-vts-8.1_r7android-vts-8.1_r6android-vts-8.1_r5android-vts-8.1_r4android-vts-8.1_r3android-vts-8.1_r14android-vts-8.1_r13android-vts-8.1_r12android-vts-8.1_r11android-vts-8.1_r10android-vts-8.0_r9android-vts-8.0_r8android-vts-8.0_r7android-vts-8.0_r6android-vts-8.0_r2android-vts-8.0_r13android-vts-8.0_r12android-vts-8.0_r11android-vts-8.0_r10android-vts-8.0_r1android-security-8.1.0_r93android-security-8.1.0_r92android-security-8.1.0_r91android-security-8.1.0_r90android-security-8.1.0_r89android-security-8.1.0_r88android-security-8.1.0_r87android-security-8.1.0_r86android-security-8.1.0_r85android-security-8.1.0_r84android-security-8.1.0_r83android-security-8.1.0_r82android-security-8.0.0_r54android-security-8.0.0_r53android-security-8.0.0_r52android-o-mr1-preview-2android-o-mr1-preview-1android-cts-8.1_r9android-cts-8.1_r8android-cts-8.1_r7android-cts-8.1_r6android-cts-8.1_r5android-cts-8.1_r4android-cts-8.1_r3android-cts-8.1_r25android-cts-8.1_r24android-cts-8.1_r23android-cts-8.1_r22android-cts-8.1_r21android-cts-8.1_r20android-cts-8.1_r2android-cts-8.1_r19android-cts-8.1_r18android-cts-8.1_r17android-cts-8.1_r16android-cts-8.1_r15android-cts-8.1_r14android-cts-8.1_r13android-cts-8.1_r12android-cts-8.1_r11android-cts-8.1_r10android-cts-8.1_r1android-cts-8.0_r9android-cts-8.0_r8android-cts-8.0_r7android-cts-8.0_r6android-cts-8.0_r5android-cts-8.0_r4android-cts-8.0_r3android-cts-8.0_r26android-cts-8.0_r25android-cts-8.0_r24android-cts-8.0_r23android-cts-8.0_r22android-cts-8.0_r21android-cts-8.0_r20android-cts-8.0_r2android-cts-8.0_r19android-cts-8.0_r18android-cts-8.0_r17android-cts-8.0_r16android-cts-8.0_r15android-cts-8.0_r14android-cts-8.0_r13android-cts-8.0_r12android-cts-8.0_r11android-cts-8.0_r10android-cts-8.0_r1android-8.1.0_r9android-8.1.0_r81android-8.1.0_r80android-8.1.0_r8android-8.1.0_r79android-8.1.0_r78android-8.1.0_r77android-8.1.0_r76android-8.1.0_r75android-8.1.0_r74android-8.1.0_r73android-8.1.0_r72android-8.1.0_r71android-8.1.0_r70android-8.1.0_r7android-8.1.0_r69android-8.1.0_r68android-8.1.0_r67android-8.1.0_r66android-8.1.0_r65android-8.1.0_r64android-8.1.0_r63android-8.1.0_r62android-8.1.0_r61android-8.1.0_r60android-8.1.0_r6android-8.1.0_r53android-8.1.0_r52android-8.1.0_r51android-8.1.0_r50android-8.1.0_r5android-8.1.0_r48android-8.1.0_r47android-8.1.0_r46android-8.1.0_r45android-8.1.0_r43android-8.1.0_r42android-8.1.0_r41android-8.1.0_r40android-8.1.0_r4android-8.1.0_r39android-8.1.0_r38android-8.1.0_r37android-8.1.0_r36android-8.1.0_r35android-8.1.0_r33android-8.1.0_r32android-8.1.0_r31android-8.1.0_r30android-8.1.0_r3android-8.1.0_r29android-8.1.0_r28android-8.1.0_r27android-8.1.0_r26android-8.1.0_r25android-8.1.0_r23android-8.1.0_r22android-8.1.0_r21android-8.1.0_r20android-8.1.0_r2android-8.1.0_r19android-8.1.0_r18android-8.1.0_r17android-8.1.0_r16android-8.1.0_r15android-8.1.0_r14android-8.1.0_r13android-8.1.0_r12android-8.1.0_r11android-8.1.0_r10android-8.1.0_r1android-8.0.0_r9android-8.0.0_r7android-8.0.0_r51android-8.0.0_r50android-8.0.0_r49android-8.0.0_r48android-8.0.0_r47android-8.0.0_r46android-8.0.0_r45android-8.0.0_r44android-8.0.0_r43android-8.0.0_r42android-8.0.0_r41android-8.0.0_r40android-8.0.0_r4android-8.0.0_r39android-8.0.0_r38android-8.0.0_r37android-8.0.0_r36android-8.0.0_r35android-8.0.0_r34android-8.0.0_r33android-8.0.0_r32android-8.0.0_r31android-8.0.0_r30android-8.0.0_r3android-8.0.0_r29android-8.0.0_r28android-8.0.0_r27android-8.0.0_r26android-8.0.0_r25android-8.0.0_r24android-8.0.0_r23android-8.0.0_r22android-8.0.0_r21android-8.0.0_r2android-8.0.0_r17android-8.0.0_r16android-8.0.0_r15android-8.0.0_r13android-8.0.0_r12android-8.0.0_r11android-8.0.0_r10android-8.0.0_r1security-oc-releasesecurity-oc-mr1-releaseoreo-vts-releaseoreo-security-releaseoreo-releaseoreo-r6-releaseoreo-r5-releaseoreo-r4-releaseoreo-r3-releaseoreo-r2-releaseoreo-mr1-wear-releaseoreo-mr1-vts-releaseoreo-mr1-security-releaseoreo-mr1-s1-releaseoreo-mr1-releaseoreo-mr1-devoreo-mr1-cuttlefish-testingoreo-mr1-cts-releaseoreo-m8-releaseoreo-m7-releaseoreo-m6-s4-releaseoreo-m6-s3-releaseoreo-m6-s2-releaseoreo-m5-releaseoreo-m4-s9-releaseoreo-m4-s8-releaseoreo-m4-s7-releaseoreo-m4-s6-releaseoreo-m4-s5-releaseoreo-m4-s4-releaseoreo-m4-s3-releaseoreo-m4-s2-releaseoreo-m4-s12-releaseoreo-m4-s11-releaseoreo-m4-s10-releaseoreo-m4-s1-releaseoreo-m3-releaseoreo-m2-s5-releaseoreo-m2-s4-releaseoreo-m2-s3-releaseoreo-m2-s2-releaseoreo-m2-s1-releaseoreo-m2-releaseoreo-dr3-releaseoreo-dr2-releaseoreo-dr1-releaseoreo-dr1-devoreo-devoreo-cts-release
Original sha1: 93fd8e35a521a7737a030e17abd488fcd0d3b948 Credits: yaochen@ Bug: 32118797 Test: Manual. Change-Id: I849949b27653abd1b03c5e59579873d639069b49
-rw-r--r--Android.mk30
-rw-r--r--AndroidManifest.xml52
-rw-r--r--res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--res/drawable-xxhdpi/ic_launcher.pngbin0 -> 19388 bytes
-rw-r--r--res/layout/activity_main.xml30
-rw-r--r--res/layout/folder_entry.xml38
-rw-r--r--res/layout/folder_list.xml48
-rw-r--r--res/values-v21/styles.xml20
-rw-r--r--res/values-w820dp/dimens.xml22
-rw-r--r--res/values/dimens.xml21
-rw-r--r--res/values/strings.xml22
-rw-r--r--res/values/styles.xml22
-rw-r--r--src/com/android/car/systemupdater/DeviceListFragment.java102
-rw-r--r--src/com/android/car/systemupdater/FileAdapter.java78
-rw-r--r--src/com/android/car/systemupdater/SystemUpdaterActivity.java305
17 files changed, 790 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..dea6312
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2015 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := SystemUpdater
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..fcb00fc
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.car.systemupdater" >
+ <uses-sdk android:minSdkVersion="21" />
+ <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" />
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true"/>
+
+ <application
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher">
+ <activity
+ android:name="com.android.car.systemupdater.SystemUpdaterActivity"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml
new file mode 100644
index 0000000..b077f52
--- /dev/null
+++ b/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+ <FrameLayout
+ android:id="@+id/device_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</RelativeLayout>
diff --git a/res/layout/folder_entry.xml b/res/layout/folder_entry.xml
new file mode 100644
index 0000000..0fec3c0
--- /dev/null
+++ b/res/layout/folder_entry.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="20dp"
+ android:gravity="center"
+ android:textSize="32sp">
+ </TextView>
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:gravity="center"
+ android:textSize="32sp">
+ </TextView>
+</LinearLayout>
diff --git a/res/layout/folder_list.xml b/res/layout/folder_list.xml
new file mode 100644
index 0000000..cb651a3
--- /dev/null
+++ b/res/layout/folder_list.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+ <Button
+ android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/back"
+ android:layout_gravity="left"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="48dp"
+ android:layout_gravity="center" />
+ </FrameLayout>
+ <ListView
+ android:id="@+id/folder_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:gravity="center"
+ android:numColumns="auto_fit"
+ android:verticalSpacing="10dp"
+ android:drawSelectorOnTop="true"
+ android:stretchMode="columnWidth" />
+</LinearLayout>
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
new file mode 100644
index 0000000..1dd3c66
--- /dev/null
+++ b/res/values-v21/styles.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<resources>
+ <style name="AppTheme" parent="android:Theme.Material.Light">
+ </style>
+</resources>
diff --git a/res/values-w820dp/dimens.xml b/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..0ef6ab4
--- /dev/null
+++ b/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..2688318
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..f820d1c
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<resources>
+ <string name="app_name">SystemUpdater</string>
+ <string name="action_settings">Settings</string>
+ <string name="title">Mounted Volumes</string>
+ <string name="back">Go Back</string>
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..de155d8
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<resources>
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
diff --git a/src/com/android/car/systemupdater/DeviceListFragment.java b/src/com/android/car/systemupdater/DeviceListFragment.java
new file mode 100644
index 0000000..cb0d43f
--- /dev/null
+++ b/src/com/android/car/systemupdater/DeviceListFragment.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 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.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+
+public class DeviceListFragment extends Fragment {
+ private ListView mFolderListView;
+ private SystemUpdaterActivity mActivity;
+ private File[] mFileNames = new File[0];
+ private FileAdapter mAdapter;
+ private Button mBackButton;
+ private TextView mTitle;
+ private String mTitleText;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActivity = (SystemUpdaterActivity) getActivity();
+ mAdapter = new FileAdapter(mActivity, R.layout.folder_entry, mFileNames);
+ }
+ @Override
+ public View onCreateView(LayoutInflater inflater, final ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.folder_list, container, false);
+ mTitle = (TextView) v.findViewById(R.id.title);
+ if (mTitleText != null) {
+ mTitle.setText(mTitleText);
+ }
+ mFolderListView = (ListView) v.findViewById(R.id.folder_list);
+ mFolderListView.setAdapter(mAdapter);
+ mFolderListView.setOnItemClickListener(mItemClickListener);
+ mBackButton = (Button) v.findViewById(R.id.back);
+ mBackButton.setOnClickListener(mBackButtonListener);
+ return v;
+ }
+
+ public void updateList(File[] locations) {
+ if (locations != null) {
+ mFileNames = locations;
+ if (mAdapter != null) {
+ mAdapter.setLocations(mFileNames);
+ }
+ }
+ }
+
+ public void updateTitle(String title) {
+ if (mTitle != null) {
+ mTitle.setText(title);
+ } else {
+ mTitleText = title;
+ }
+ }
+
+ private final View.OnClickListener mBackButtonListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mActivity.onBackPressed();
+ }
+ };
+
+ private final AdapterView.OnItemClickListener mItemClickListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view,
+ int position, long id) {
+ if (mFileNames[position].getName().endsWith(".zip")) {
+ mActivity.checkPackage(mFileNames[position]);
+ } else if (mFileNames[position].isDirectory()) {
+ mActivity.showFolderContent(mFileNames[position]);
+ } else {
+ Toast.makeText(mActivity, "This is not a valid file for updating",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+}
diff --git a/src/com/android/car/systemupdater/FileAdapter.java b/src/com/android/car/systemupdater/FileAdapter.java
new file mode 100644
index 0000000..1b0ecd6
--- /dev/null
+++ b/src/com/android/car/systemupdater/FileAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import java.io.File;
+
+
+public class FileAdapter extends ArrayAdapter<File> {
+ private final Context mContext;
+ private File[] mLocations;
+ private final int mLayoutResourceId;
+
+ public FileAdapter(Context c, int layoutResourceId, File[] locations) {
+ super(c, layoutResourceId, locations);
+ mContext = c;
+ this.mLayoutResourceId = layoutResourceId;
+ this.mLocations = locations;
+ }
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder vh = new ViewHolder();
+ if (convertView == null) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ convertView = inflater.inflate(mLayoutResourceId, parent, false);
+ vh.textView = (TextView) convertView.findViewById(R.id.text);
+ vh.descriptionView = (TextView) convertView.findViewById(R.id.description);
+ convertView.setTag(vh);
+ } else {
+ vh = (ViewHolder) convertView.getTag();
+ }
+ if (mLocations[position] != null) {
+ vh.textView.setText(mLocations[position].getAbsolutePath());
+ if (mLocations[position].getAbsolutePath().endsWith(".zip")
+ || mLocations[position].isDirectory()) {
+ vh.textView.setTextColor(Color.GREEN);
+ } else {
+ vh.textView.setTextColor(Color.GRAY);
+ }
+ }
+ return convertView;
+ }
+
+ @Override
+ public int getCount() {
+ return mLocations.length;
+ }
+
+ public void setLocations(File[] locations) {
+ mLocations = locations;
+ notifyDataSetChanged();
+ }
+
+ static class ViewHolder {
+ TextView textView;
+ TextView descriptionView;
+ }
+}
diff --git a/src/com/android/car/systemupdater/SystemUpdaterActivity.java b/src/com/android/car/systemupdater/SystemUpdaterActivity.java
new file mode 100644
index 0000000..e442731
--- /dev/null
+++ b/src/com/android/car/systemupdater/SystemUpdaterActivity.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2015 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.app.Activity;
+import android.app.AlertDialog;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RecoverySystem;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.util.List;
+
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+
+/**
+ * A prototype of performing system update using an ota package on internal or external storage.
+ * TODO(yaochen): Move the code to a proper location and let it extend CarActivity once available.
+ */
+public class SystemUpdaterActivity extends Activity {
+ private static final String TAG = "SystemUpdaterActivity";
+ private static final boolean DEBUG = true;
+ private static final String UPDATE_FILE_NAME = "update.zip";
+
+ private final Handler mHandler = new Handler();
+ private StorageManager mStorageManager = null;
+ private ProgressDialog mVerifyPackageDialog = null;
+
+
+ private final StorageEventListener mListener = new StorageEventListener() {
+ @Override
+ public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+ if (DEBUG) {
+ Log.d(TAG, "onVolumeMetadataChanged " + oldState + " " + newState
+ + " " + vol.toString());
+ }
+ showMountedVolumes();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ if (mStorageManager == null) {
+ Log.w(TAG, "Failed to get StorageManager");
+ Toast.makeText(this, "Cannot get StorageManager!", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mStorageManager != null) {
+ mStorageManager.registerListener(mListener);
+ showMountedVolumes();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mStorageManager != null) {
+ mStorageManager.unregisterListener(mListener);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (getFragmentManager().getBackStackEntryCount() > 0) {
+ getFragmentManager().popBackStackImmediate();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ public void showMountedVolumes() {
+ if (mStorageManager == null) {
+ return;
+ }
+ final List<VolumeInfo> vols = mStorageManager.getVolumes();
+ File[] files = new File[vols.size()];
+ int i = 0;
+ for (VolumeInfo vol : vols) {
+ File path = vol.getPathForUser(getUserId());
+ if (vol.getState() != VolumeInfo.STATE_MOUNTED || path == null) {
+ continue;
+ }
+ files[i++] = path;
+ }
+ getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ DeviceListFragment frag = new DeviceListFragment();
+ frag.updateList(files);
+ frag.updateTitle(getString(R.string.title));
+ getFragmentManager().beginTransaction()
+ .replace(R.id.device_container, frag).commit();
+ }
+
+ public void showFolderContent(final File location) {
+ if (!location.isDirectory()) {
+ return;
+ }
+ AsyncTask<String, Void, File[]> readFilesTask = new AsyncTask<String, Void, File[]>() {
+ @Override
+ protected File[] doInBackground(String... strings) {
+ File f = new File(strings[0]);
+ /* if we want to filter files, use
+ File[] files = f.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ return true;
+ }
+ }); */
+ return f.listFiles();
+ }
+
+ @Override
+ protected void onPostExecute(File[] results) {
+ super.onPostExecute(results);
+ if (results == null) {
+ results = new File[0];
+ }
+ DeviceListFragment frag = new DeviceListFragment();
+ frag.updateTitle(location.getAbsolutePath());
+ frag.updateList(results);
+ getFragmentManager().beginTransaction()
+ .replace(R.id.device_container, frag).addToBackStack(null).commit();
+ }
+ };
+ readFilesTask.execute(location.getAbsolutePath());
+ }
+
+ public void checkPackage(File file) {
+ mVerifyPackageDialog = new ProgressDialog(this);
+ mVerifyPackageDialog.setTitle("Verifying... " + file.getAbsolutePath());
+
+ final PackageVerifier verifyPackage = new PackageVerifier();
+ verifyPackage.execute(file);
+ mVerifyPackageDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ verifyPackage.cancel(true);
+ }
+ });
+ mVerifyPackageDialog.setProgressStyle(mVerifyPackageDialog.STYLE_HORIZONTAL);
+ mVerifyPackageDialog.setMax(100);
+ mVerifyPackageDialog.setProgress(0);
+ mVerifyPackageDialog.show();
+ }
+
+ private class PackageVerifier extends AsyncTask<File, Void, Exception> {
+ File mFile;
+
+ @Override
+ protected Exception doInBackground(File... files) {
+ File file = files[0];
+ mFile = file;
+ try {
+ RecoverySystem.verifyPackage(file, mProgressListener, null);
+ } catch (GeneralSecurityException e) {
+ Log.e(TAG, "Security Exception in verifying package " + file, e);
+ return e;
+ } catch (IOException e) {
+ Log.e(TAG, "IO Exception in verifying package " + file, e);
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Exception result) {
+ mVerifyPackageDialog.cancel();
+ if (result == null) {
+ mVerifyPackageDialog = new ProgressDialog(SystemUpdaterActivity.this);
+ mVerifyPackageDialog.setTitle("Copying " + mFile.getName()
+ + " to " + getCacheDir() + "/" + UPDATE_FILE_NAME);
+ mVerifyPackageDialog.setProgressStyle(mVerifyPackageDialog.STYLE_HORIZONTAL);
+ mVerifyPackageDialog.setMax((int) (mFile.length() / 1024));
+ mVerifyPackageDialog.show();
+ new CopyFile().execute(mFile);
+ } else {
+ AlertDialog.Builder doneDialog =
+ new AlertDialog.Builder(SystemUpdaterActivity.this);
+ doneDialog.setMessage("Verification failed! " + result.getMessage()).show();
+ }
+ }
+ }
+
+
+ private class CopyFile extends AsyncTask<File, Void, Exception> {
+ @Override
+ protected Exception doInBackground(File... files) {
+ File file = files[0];
+ if (getCacheDir().getFreeSpace() < file.length()) {
+ return new IOException("Not enough cache space!");
+ }
+ File dest = new File(getCacheDir(), UPDATE_FILE_NAME);
+ try {
+ copy(file, dest);
+ } catch (IOException e) {
+ Log.e(TAG, "Error when coping file to cache", e);
+ dest.delete();
+ return new IOException(e.getMessage());
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Exception result) {
+ mVerifyPackageDialog.cancel();
+ AlertDialog.Builder doneDialog = new AlertDialog.Builder(SystemUpdaterActivity.this);
+
+ doneDialog.setMessage("Copy " + (result == null ? "completed!" : "failed!"
+ + result.getMessage()));
+
+ if (result == null) {
+ doneDialog.setPositiveButton("Start system update",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ try {
+ RecoverySystem.installPackage(SystemUpdaterActivity.this,
+ new File(getCacheDir(), UPDATE_FILE_NAME));
+ } catch (IOException e) {
+ Log.e(TAG, "IOException in installing ota package");
+ Toast.makeText(SystemUpdaterActivity.this,
+ "IOException in installing ota package ",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+ } else {
+ Log.e(TAG, "Copy failed!", result);
+ }
+ doneDialog.create().show();
+ }
+ }
+
+ private void copy(File src, File dst) throws IOException {
+ InputStream in = new FileInputStream(src);
+ OutputStream out = new FileOutputStream(dst);
+ try {
+ // Transfer bytes from in to out
+ byte[] buf = new byte[0x10000]; // 64k
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mVerifyPackageDialog.incrementProgressBy(1);
+ }
+ });
+ }
+ } finally {
+ in.close();
+ out.close();
+ }
+ }
+
+ private final RecoverySystem.ProgressListener mProgressListener =
+ new RecoverySystem.ProgressListener() {
+ @Override
+ public void onProgress(final int i) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVerifyPackageDialog != null) {
+ mVerifyPackageDialog.setProgress(i);
+ }
+ }
+ });
+ }
+ };
+}