Merge change 556 into donut
* changes:
Fixed a bug in XML parser handler where some words were being split in two. Fixes #1812655.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7d49c4a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+*~
+tools/eclipse/plugins/com.android.ide.eclipse.adt/androidprefs.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/jarutils.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/kxml2-2.3.0.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/layoutlib_api.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/layoutlib_utils.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/ninepatch.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdklib.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdkstats.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdkuilib.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/add.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/backward.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/clear.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/d.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-attach.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-error.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-wait.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/delete.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/device.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/down.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/e.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/edit.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/empty.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/forward.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/gc.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/halt.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/heap.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/i.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/importBug.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/load.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/pause.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/play.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/pull.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/push.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/save.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/thread.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/up.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/v.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/w.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/warning.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jcommon-1.0.12.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jfreechart-1.0.9-swt.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jfreechart-1.0.9.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ddmlib
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ddmuilib
+tools/eclipse/plugins/com.android.ide.eclipse.tests/kxml2-2.3.0.jar
+tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ddmlib
diff --git a/apps/Development/AndroidManifest.xml b/apps/Development/AndroidManifest.xml
index 879e7d6..316cbf5 100644
--- a/apps/Development/AndroidManifest.xml
+++ b/apps/Development/AndroidManifest.xml
@@ -36,7 +36,6 @@
android:icon="@drawable/ic_launcher_devtools">
<uses-library android:name="android.test.runner" />
- <uses-library android:name="com.google.android.maps" />
<activity android:name="Development" android:label="Dev Tools"
android:icon="@drawable/ic_launcher_devtools">
diff --git a/apps/Development/res/layout/development_settings.xml b/apps/Development/res/layout/development_settings.xml
index 0486748..4f185aa 100644
--- a/apps/Development/res/layout/development_settings.xml
+++ b/apps/Development/res/layout/development_settings.xml
@@ -142,13 +142,6 @@
android:layout_below="@id/font_hinting"
android:layout_alignParentLeft="true"
android:text="@string/development_settings_show_xmpp_text" />
-
- <CheckBox android:id="@+id/show_maps_compass"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/show_xmpp"
- android:layout_alignParentLeft="true"
- android:text="@string/development_settings_show_maps_compass_text" />
</RelativeLayout>
diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml
index 8d343ae..74f411d 100644
--- a/apps/Development/res/values/strings.xml
+++ b/apps/Development/res/values/strings.xml
@@ -84,7 +84,6 @@
<string name="development_settings_show_xmpp_text">Show GTalk service connection status</string>
<string name="development_settings_debug_app_label_text">Debug App:</string>
<string name="development_settings_show_sleep_text">Show sleep state on LED</string>
- <string name="development_settings_show_maps_compass_text">Show compass in Maps</string>
<string name="development_settings_keep_screen_on_text">Keep screen on while plugged in</string>
<string name="monkey_screen_initialActivity_text"></string>
diff --git a/apps/Development/src/com/android/development/DevelopmentSettings.java b/apps/Development/src/com/android/development/DevelopmentSettings.java
index cdc1e0a..3b4848a 100644
--- a/apps/Development/src/com/android/development/DevelopmentSettings.java
+++ b/apps/Development/src/com/android/development/DevelopmentSettings.java
@@ -57,7 +57,6 @@
private CheckBox mShowUpdatesCB;
private CheckBox mShowBackgroundCB;
private CheckBox mShowSleepCB;
- private CheckBox mShowMapsCompassCB;
private CheckBox mShowXmppCB;
private Spinner mMaxProcsSpinner;
private Spinner mWindowAnimationScaleSpinner;
@@ -69,7 +68,6 @@
private boolean mAlwaysFinish;
private int mProcessLimit;
private boolean mShowSleep;
- private boolean mShowMapsCompass;
private boolean mShowXmpp;
private AnimationScaleSelectedListener mWindowAnimationScale
= new AnimationScaleSelectedListener(0);
@@ -106,8 +104,6 @@
mShowBackgroundCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1003));
mShowSleepCB = (CheckBox)findViewById(R.id.show_sleep);
mShowSleepCB.setOnClickListener(mShowSleepClicked);
- mShowMapsCompassCB = (CheckBox)findViewById(R.id.show_maps_compass);
- mShowMapsCompassCB.setOnClickListener(mShowMapsCompassClicked);
mShowXmppCB = (CheckBox)findViewById(R.id.show_xmpp);
mShowXmppCB.setOnClickListener(mShowXmppClicked);
mMaxProcsSpinner = (Spinner)findViewById(R.id.max_procs);
@@ -172,7 +168,6 @@
updateSharedOptions();
updateFlingerOptions();
updateSleepOptions();
- updateMapsCompassOptions();
updateXmppOptions();
try {
@@ -293,31 +288,6 @@
mShowSleepCB.setChecked(mShowSleep);
}
- private void writeMapsCompassOptions() {
- try {
- Context c = createPackageContext("com.google.android.apps.maps", 0);
- c.getSharedPreferences("extra-features", MODE_WORLD_WRITEABLE)
- .edit()
- .putBoolean("compass", mShowMapsCompass)
- .commit();
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed setting maps compass");
- e.printStackTrace();
- }
- }
-
- private void updateMapsCompassOptions() {
- try {
- Context c = createPackageContext("com.google.android.apps.maps", 0);
- mShowMapsCompass = c.getSharedPreferences("extra-features", MODE_WORLD_READABLE)
- .getBoolean("compass", false);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed reading maps compass");
- e.printStackTrace();
- }
- mShowMapsCompassCB.setChecked(mShowMapsCompass);
- }
-
private void writeXmppOptions() {
Settings.System.setShowGTalkServiceStatus(getContentResolver(), mShowXmpp);
}
@@ -410,16 +380,6 @@
}
};
- private View.OnClickListener mShowMapsCompassClicked =
- new View.OnClickListener() {
- public void onClick(View v) {
- mShowMapsCompass = ((CheckBox)v).isChecked();
- writeMapsCompassOptions();
- updateMapsCompassOptions();
- }
- };
-
-
private View.OnClickListener mShowXmppClicked = new View.OnClickListener() {
public void onClick(View v) {
mShowXmpp = ((CheckBox)v).isChecked();
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index fe0de99..5e9f07c 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -130,7 +130,8 @@
float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
MonkeyEventSource mEventSource;
-
+ private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
+
/**
* Monitor operations happening in the system.
*/
@@ -222,14 +223,14 @@
return 1;
}
}
-
+
/**
* Run the procrank tool to insert system status information into the debug report.
*/
private void reportProcRank() {
commandLineReport("procrank", "procrank");
}
-
+
/**
* Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the asynchronous
* report writing complete.
@@ -401,7 +402,9 @@
signalPersistentProcesses();
}
+ mNetworkMonitor.start();
int crashedAtCycle = runMonkeyCycles();
+ mNetworkMonitor.stop();
synchronized (this) {
if (mRequestAnrTraces) {
@@ -423,6 +426,7 @@
try {
mAm.setActivityWatcher(null);
+ mNetworkMonitor.unregister(mAm);
} catch (RemoteException e) {
// just in case this was latent (after mCount cycles), make sure
// we report it
@@ -442,6 +446,9 @@
System.out.print(" flips=");
System.out.println(mDroppedFlipEvents);
}
+
+ // report network stats
+ mNetworkMonitor.dump();
if (crashedAtCycle < mCount - 1) {
System.err.println("** System appears to have crashed at event "
@@ -602,6 +609,7 @@
try {
mAm.setActivityWatcher(new ActivityWatcher());
+ mNetworkMonitor.register(mAm);
} catch (RemoteException e) {
System.err.println("** Failed talking with activity manager!");
return false;
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
new file mode 100644
index 0000000..e8d9812
--- /dev/null
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
@@ -0,0 +1,105 @@
+/**
+** Copyright 2007, 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.commands.monkey;
+
+import android.app.IActivityManager;
+import android.app.IIntentReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+/**
+ * Class for monitoring network connectivity during monkey runs.
+ */
+public class MonkeyNetworkMonitor extends IIntentReceiver.Stub {
+ private static final boolean LDEBUG = false;
+ private final IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+ private long mCollectionStartTime; // time we started collecting data
+ private long mEventTime; // time of last event (connect, disconnect, etc.)
+ private int mLastNetworkType = -1; // unknown
+ private long mWifiElapsedTime = 0; // accumulated time spent on wifi since start()
+ private long mMobileElapsedTime = 0; // accumulated time spent on mobile since start()
+ private long mElapsedTime = 0; // amount of time spent between start() and stop()
+
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered) throws RemoteException {
+ NetworkInfo ni = (NetworkInfo) intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (LDEBUG) System.out.println("Network state changed: "
+ + "type=" + ni.getType() + ", state=" + ni.getState());
+ updateNetworkStats();
+ if (NetworkInfo.State.CONNECTED == ni.getState()) {
+ if (LDEBUG) System.out.println("Network connected");
+ mLastNetworkType = ni.getType();
+ } else if (NetworkInfo.State.DISCONNECTED == ni.getState()) {
+ if (LDEBUG) System.out.println("Network not connected");
+ mLastNetworkType = -1; // unknown since we're disconnected
+ }
+ mEventTime = SystemClock.elapsedRealtime();
+ }
+
+ private void updateNetworkStats() {
+ long timeNow = SystemClock.elapsedRealtime();
+ long delta = timeNow - mEventTime;
+ switch (mLastNetworkType) {
+ case ConnectivityManager.TYPE_MOBILE:
+ if (LDEBUG) System.out.println("Adding to mobile: " + delta);
+ mMobileElapsedTime += delta;
+ break;
+ case ConnectivityManager.TYPE_WIFI:
+ if (LDEBUG) System.out.println("Adding to wifi: " + delta);
+ mWifiElapsedTime += delta;
+ break;
+ default:
+ if (LDEBUG) System.out.println("Unaccounted for: " + delta);
+ break;
+ }
+ mElapsedTime = timeNow - mCollectionStartTime;
+ }
+
+ public void start() {
+ mWifiElapsedTime = 0;
+ mMobileElapsedTime = 0;
+ mElapsedTime = 0;
+ mEventTime = mCollectionStartTime = SystemClock.elapsedRealtime();
+ }
+
+ public void register(IActivityManager am) throws RemoteException {
+ if (LDEBUG) System.out.println("registering Receiver");
+ am.registerReceiver(null, this, filter, null);
+ }
+
+ public void unregister(IActivityManager am) throws RemoteException {
+ if (LDEBUG) System.out.println("unregistering Receiver");
+ am.unregisterReceiver(this);
+ }
+
+ public void stop() {
+ updateNetworkStats();
+ }
+
+ public void dump() {
+ System.out.println("## Network stats: elapsed time=" + mElapsedTime + "ms ("
+ + mMobileElapsedTime + "ms mobile, "
+ + mWifiElapsedTime + "ms wifi, "
+ + (mElapsedTime - mMobileElapsedTime - mWifiElapsedTime) + "ms not connected)");
+ }
+ }
\ No newline at end of file
diff --git a/docs/copyright-templates/asm.txt b/docs/copyright-templates/asm.txt
new file mode 100644
index 0000000..95a9022
--- /dev/null
+++ b/docs/copyright-templates/asm.txt
@@ -0,0 +1,13 @@
+; Copyright (C) 2009 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.
diff --git a/docs/copyright-templates/bash.txt b/docs/copyright-templates/bash.txt
new file mode 100644
index 0000000..ecf510e
--- /dev/null
+++ b/docs/copyright-templates/bash.txt
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 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.
diff --git a/docs/copyright-templates/bsd/c.txt b/docs/copyright-templates/bsd/c.txt
new file mode 100644
index 0000000..c574b4a
--- /dev/null
+++ b/docs/copyright-templates/bsd/c.txt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/docs/copyright-templates/c.txt b/docs/copyright-templates/c.txt
new file mode 100644
index 0000000..7cd7083
--- /dev/null
+++ b/docs/copyright-templates/c.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009 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.
+ */
diff --git a/docs/copyright-templates/java.txt b/docs/copyright-templates/java.txt
new file mode 100644
index 0000000..7cd7083
--- /dev/null
+++ b/docs/copyright-templates/java.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009 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.
+ */
diff --git a/docs/copyright-templates/make.txt b/docs/copyright-templates/make.txt
new file mode 100644
index 0000000..e4b376e
--- /dev/null
+++ b/docs/copyright-templates/make.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2009 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.
diff --git a/docs/copyright-templates/plain.txt b/docs/copyright-templates/plain.txt
new file mode 100644
index 0000000..4549ecb
--- /dev/null
+++ b/docs/copyright-templates/plain.txt
@@ -0,0 +1,13 @@
+Copyright (C) 2009 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.
diff --git a/docs/copyright-templates/sh.txt b/docs/copyright-templates/sh.txt
new file mode 100644
index 0000000..e53137d
--- /dev/null
+++ b/docs/copyright-templates/sh.txt
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Copyright (C) 2009 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.
diff --git a/docs/copyright-templates/xml.txt b/docs/copyright-templates/xml.txt
new file mode 100644
index 0000000..2a9d2b6
--- /dev/null
+++ b/docs/copyright-templates/xml.txt
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
diff --git a/pdk/Pdk.mk b/pdk/Pdk.mk
index 584eccf..29a08a3 100644
--- a/pdk/Pdk.mk
+++ b/pdk/Pdk.mk
@@ -101,7 +101,7 @@
# copy-one-file defines the actual rule.
$(foreach template,$(pdk_templates), \
$(eval _chFrom := $(template)) \
- $(eval _chTo := $(pdk_docs_dest_dir)/$(notdir $(template))) \
+ $(eval _chTo := $(pdk_docs_dest_dir)/$(patsubst $(pdk_templates_dir)/%,%,$(template))) \
$(eval $(call copy-one-header,$(_chFrom),$(_chTo))) \
$(eval all_copied_pdk_templates: $(_chTo)) \
)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
index 1a4b5e4..3094afe 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
@@ -20,6 +20,8 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
@@ -29,6 +31,7 @@
import android.widget.ListView;
import java.util.ArrayList;
+import java.util.List;
/**
* Sample code that invokes the speech recognition intent API.
@@ -54,8 +57,16 @@
mList = (ListView) findViewById(R.id.list);
- // Attach actions to buttons
- speakButton.setOnClickListener(this);
+ // Check to see if a recognition activity is present
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> activities = pm.queryIntentActivities(
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
+ if (activities.size() != 0) {
+ speakButton.setOnClickListener(this);
+ } else {
+ speakButton.setEnabled(false);
+ speakButton.setText("Recognizer not present");
+ }
}
/**
@@ -71,7 +82,6 @@
* Fire an intent to start the speech recognition activity.
*/
private void startVoiceRecognitionActivity() {
- //TODO Get these values from constants
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
@@ -85,6 +95,7 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) {
+ // Fill the list view with the strings the recognizer thought it could have heard
ArrayList<String> matches = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
mList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java
new file mode 100644
index 0000000..c492e3f
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2008 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.example.android.apis.graphics;
+
+import static android.opengl.GLES10.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.opengl.GLUtils;
+import android.os.SystemClock;
+
+import com.example.android.apis.R;
+
+/**
+ * A GLSurfaceView.Renderer that uses the Android-specific
+ * android.opengl.GLESXXX static OpenGL ES APIs. The static APIs
+ * expose more of the OpenGL ES features than the
+ * javax.microedition.khronos.opengles APIs, and also
+ * provide a programming model that is closer to the C OpenGL ES APIs, which
+ * may make it easier to reuse code and documentation written for the
+ * C OpenGL ES APIs.
+ *
+ */
+public class StaticTriangleRenderer implements GLSurfaceView.Renderer{
+
+ public StaticTriangleRenderer(Context context) {
+ mContext = context;
+ mTriangle = new Triangle();
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ glDisable(GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT,
+ GL_FASTEST);
+
+ glClearColor(.5f, .5f, .5f, 1);
+ glShadeModel(GL_SMOOTH);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_TEXTURE_2D);
+
+ /*
+ * Create our texture. This has to be done each time the
+ * surface is created.
+ */
+
+ int[] textures = new int[1];
+ glGenTextures(1, textures, 0);
+
+ mTextureID = textures[0];
+ glBindTexture(GL_TEXTURE_2D, mTextureID);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER,
+ GL_LINEAR);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_EDGE);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ GL_REPLACE);
+
+ InputStream is = mContext.getResources()
+ .openRawResource(R.drawable.robot);
+ Bitmap bitmap;
+ try {
+ bitmap = BitmapFactory.decodeStream(is);
+ } finally {
+ try {
+ is.close();
+ } catch(IOException e) {
+ // Ignore.
+ }
+ }
+
+ GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+ bitmap.recycle();
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ glDisable(GL_DITHER);
+
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ GL_MODULATE);
+
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureID);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ GL_REPEAT);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ GL_REPEAT);
+
+ long time = SystemClock.uptimeMillis() % 4000L;
+ float angle = 0.090f * ((int) time);
+
+ glRotatef(angle, 0, 0, 1.0f);
+
+ mTriangle.draw(gl);
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ glViewport(0, 0, w, h);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) w / h;
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustumf(-ratio, ratio, -1, 1, 3, 7);
+ }
+
+ private Context mContext;
+ private Triangle mTriangle;
+ private int mTextureID;
+
+ static class Triangle {
+ public Triangle() {
+
+ // Buffers to be passed to gl*Pointer() functions
+ // must be direct, i.e., they must be placed on the
+ // native heap where the garbage collector cannot
+ // move them.
+ //
+ // Buffers with multi-byte datatypes (e.g., short, int, float)
+ // must have their byte order set to native order
+
+ ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
+ vbb.order(ByteOrder.nativeOrder());
+ mFVertexBuffer = vbb.asFloatBuffer();
+
+ ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
+ tbb.order(ByteOrder.nativeOrder());
+ mTexBuffer = tbb.asFloatBuffer();
+
+ ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
+ ibb.order(ByteOrder.nativeOrder());
+ mIndexBuffer = ibb.asShortBuffer();
+
+ // A unit-sided equilateral triangle centered on the origin.
+ float[] coords = {
+ // X, Y, Z
+ -0.5f, -0.25f, 0,
+ 0.5f, -0.25f, 0,
+ 0.0f, 0.559016994f, 0
+ };
+
+ for (int i = 0; i < VERTS; i++) {
+ for(int j = 0; j < 3; j++) {
+ mFVertexBuffer.put(coords[i*3+j] * 2.0f);
+ }
+ }
+
+ for (int i = 0; i < VERTS; i++) {
+ for(int j = 0; j < 2; j++) {
+ mTexBuffer.put(coords[i*3+j] * 2.0f + 0.5f);
+ }
+ }
+
+ for(int i = 0; i < VERTS; i++) {
+ mIndexBuffer.put((short) i);
+ }
+
+ mFVertexBuffer.position(0);
+ mTexBuffer.position(0);
+ mIndexBuffer.position(0);
+ }
+
+ public void draw(GL10 gl) {
+ glFrontFace(GL_CCW);
+ glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);
+ glEnable(GL_TEXTURE_2D);
+ glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);
+ glDrawElements(GL_TRIANGLE_STRIP, VERTS,
+ GL_UNSIGNED_SHORT, mIndexBuffer);
+ }
+
+ private final static int VERTS = 3;
+
+ private FloatBuffer mFVertexBuffer;
+ private FloatBuffer mTexBuffer;
+ private ShortBuffer mIndexBuffer;
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
index e5b06f4..7d7cd4a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
@@ -27,7 +27,7 @@
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
mGLView.setEGLConfigChooser(false);
- mGLView.setRenderer(new TriangleRenderer(this));
+ mGLView.setRenderer(new StaticTriangleRenderer(this));
setContentView(mGLView);
}
diff --git a/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java b/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
index 0103da8..bc174ea 100644
--- a/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
+++ b/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
@@ -24,6 +24,12 @@
*/
public class ApiDemosTest extends ActivityInstrumentationTestCase<ApiDemos> {
+ /**
+ * The first constructor parameter must refer to the package identifier of the
+ * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
+ * file. This is not necessarily the same as the java package name of the class - in fact, in
+ * some cases it may not match at all.
+ */
public ApiDemosTest() {
super("com.example.android.apis", ApiDemos.class);
}
diff --git a/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java b/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
index 91f712f..5938209 100644
--- a/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
+++ b/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
@@ -48,7 +48,12 @@
private Button mCenterButton;
private Button mRightButton;
-
+ /**
+ * The first constructor parameter must refer to the package identifier of the
+ * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
+ * file. This is not necessarily the same as the java package name of the class - in fact, in
+ * some cases it may not match at all.
+ */
public Focus2ActivityTest() {
super("com.example.android.apis", Focus2.class);
}
diff --git a/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
index 3a49a87..1b17925 100755
--- a/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
+++ b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
@@ -416,14 +416,8 @@
*/
private void initializeJetPlayer() {
- // JET info: this is what we do to undesirable instances: we release them!
- if (mJet != null)
- {
- mJet.release();
- mJet = null;
- }
-
- // JET info: let's create our JetPlayer instance using the factory
+ // JET info: let's create our JetPlayer instance using the factory.
+ // JET info: if we already had one, the same singleton is returned.
mJet = JetPlayer.getJetPlayer();
mJetPlaying = false;
diff --git a/testrunner/run_command.py b/testrunner/run_command.py
index 6b72b77..5336f33 100755
--- a/testrunner/run_command.py
+++ b/testrunner/run_command.py
@@ -3,37 +3,37 @@
#
# Copyright 2007, 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
+# 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
+# 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
+# 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.
# System imports
import os
import signal
import subprocess
-import time
import threading
+import time
# local imports
-import logger
import errors
+import logger
_abort_on_error = False
def SetAbortOnError(abort=True):
- """Sets behavior of RunCommand to throw AbortError if command process returns
+ """Sets behavior of RunCommand to throw AbortError if command process returns
a negative error code"""
global _abort_on_error
_abort_on_error = abort
-
+
def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True):
"""Spawns a subprocess to run the given shell command, and checks for
timeout_time. If return_output is True, the output of the command is returned
@@ -42,7 +42,7 @@
result = None
while True:
try:
- result = RunOnce(cmd, timeout_time=timeout_time,
+ result = RunOnce(cmd, timeout_time=timeout_time,
return_output=return_output)
except errors.WaitForResponseTimedOutError:
if retry_count == 0:
@@ -59,13 +59,13 @@
pid = []
global _abort_on_error
error_occurred = False
-
+
def Run():
if return_output:
output_dest = subprocess.PIPE
else:
# None means direct to stdout
- output_dest = None
+ output_dest = None
pipe = subprocess.Popen(
cmd,
executable='/bin/bash',
@@ -83,9 +83,9 @@
so.append("ERROR")
error_occurred = True
if pipe.returncode < 0:
- logger.SilentLog("Error: %s was terminated by signal %d" %(cmd,
+ logger.SilentLog("Error: %s was terminated by signal %d" %(cmd,
pipe.returncode))
- error_occurred = True
+ error_occurred = True
t = threading.Thread(target=Run)
t.start()
@@ -113,5 +113,26 @@
if _abort_on_error and error_occurred:
raise errors.AbortError
-
+
return "".join(so)
+
+
+def RunHostCommand(binary, valgrind=False):
+ """Run a command on the host (opt using valgrind).
+
+ Runs the host binary. Does not capture any output but it
+ returns the exit code. The command can be run under valgrind.
+
+ Args:
+ binary: basename of the file to be run. It is expected to be under
+ out/host/linux-x86/bin.
+ valgrind: If True the command will be run under valgrind.
+
+ Returns:
+ The command exit code (int)
+ """
+ full_path = os.path.join("out", "host", "linux-x86", "bin", binary)
+ if not valgrind:
+ return subprocess.call(full_path)
+ else:
+ return subprocess.call(["/usr/bin/valgrind", "-q", full_path])
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 6d6c6df..f87d451 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -44,7 +44,7 @@
# file path to android core platform tests, relative to android build root
# TODO move these test data files to another directory
- _CORE_TEST_PATH = os.path.join("development", "testrunner",
+ _CORE_TEST_PATH = os.path.join("development", "testrunner",
_TEST_FILE_NAME)
# vendor glob file path patterns to tests, relative to android
@@ -60,7 +60,7 @@
def __init__(self):
# disable logging of timestamp
self._root_path = android_build.GetTop()
- logger.SetTimestampLogging(False)
+ logger.SetTimestampLogging(False)
def _ProcessOptions(self):
"""Processes command-line options."""
@@ -290,12 +290,27 @@
# find all test files, convert unicode names to ascii, take the basename
# and drop the .cc/.cpp extension.
file_pattern = os.path.join(test_suite.GetBuildPath(), "test_*")
+ logger.SilentLog("Scanning %s" % test_suite.GetBuildPath())
file_list = []
for f in map(str, glob.glob(file_pattern)):
f = os.path.basename(f)
f = re.split(".[cp]+$", f)[0]
+ logger.SilentLog("Found %s" % f)
file_list.append(f)
+ # Run on the host
+ logger.Log("\nRunning on host")
+ for f in file_list:
+ if run_command.RunHostCommand(f) != 0:
+ logger.Log("%s... failed" % f)
+ else:
+ if run_command.RunHostCommand(f, valgrind=True) == 0:
+ logger.Log("%s... ok\t\t[valgrind: ok]" % f)
+ else:
+ logger.Log("%s... ok\t\t[valgrind: failed]" % f)
+
+ # Run on the device
+ logger.Log("\nRunning on target")
for f in file_list:
full_path = "/system/bin/%s" % f
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
index 72fe060..36bd148 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
@@ -17,10 +17,12 @@
package com.android.ide.eclipse.editors.ui.tree;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ILabelProvider;
@@ -31,10 +33,16 @@
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.part.FileEditorInput;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
/**
* A selection dialog to select the type of the new element node to
@@ -50,15 +58,25 @@
private UiElementNode mChosenRootNode;
private UiElementNode mLocalRootNode;
/** The descriptor of the elements to be displayed as root in this tree view. All elements
- * of the same type in the root will be displayed. */
+ * of the same type in the root will be displayed. Can be null. */
private ElementDescriptor[] mDescriptorFilters;
+ /** The key for the {@link #setLastUsedXmlName(Object[])}. It corresponds to the full
+ * workspace path of the currently edited file, if this can be computed. This is computed
+ * by {@link #getLastUsedXmlName(UiElementNode)}, called from the constructor. */
+ private String mLastUsedKey;
+ /** A static map of known XML Names used for a given file. The map has full workspace
+ * paths as key and XML names as values. */
+ private static final Map<String, String> sLastUsedXmlName = new HashMap<String, String>();
+ /** The potential XML Name to initially select in the selection dialog. This is computed
+ * in the constructor and set by {@link #setInitialSelection(UiElementNode)}. */
+ private String mInitialXmlName;
/**
* Creates the new item selection dialog.
*
* @param shell The parent shell for the list.
* @param labelProvider ILabelProvider for the list.
- * @param descriptorFilters The element allows at the root of the tree
+ * @param descriptorFilters The element allows at the root of the tree. Can be null.
* @param ui_node The selected node, or null if none is selected.
* @param root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node.
*/
@@ -109,6 +127,114 @@
}
}
});
+
+ // Determine the initial selection using a couple heuristics.
+
+ // First check if we can get the last used node type for this file.
+ // The heuristic is that generally one keeps adding the same kind of items to the
+ // same file, so reusing the last used item type makes most sense.
+ String xmlName = getLastUsedXmlName(root_node);
+ if (xmlName == null) {
+ // Another heuristic is to find the most used item and default to that.
+ xmlName = getMostUsedXmlName(root_node);
+ }
+ if (xmlName == null) {
+ // Finally the last heuristic is to see if there's an item with a name
+ // similar to the edited file name.
+ xmlName = getLeafFileName(root_node);
+ }
+ // Set the potential name. Selecting the right item is done later by setInitialSelection().
+ mInitialXmlName = xmlName;
+ }
+
+ /**
+ * Returns a potential XML name based on the file name.
+ * The item name is marked with an asterisk to identify it as a partial match.
+ */
+ private String getLeafFileName(UiElementNode ui_node) {
+ if (ui_node != null) {
+ AndroidEditor editor = ui_node.getEditor();
+ if (editor != null) {
+ IEditorInput editorInput = editor.getEditorInput();
+ if (editorInput instanceof FileEditorInput) {
+ IFile f = ((FileEditorInput) editorInput).getFile();
+ if (f != null) {
+ String leafName = f.getFullPath().removeFileExtension().lastSegment();
+ return "*" + leafName; //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Given a potential non-null root node, this method looks for the currently edited
+ * file path and uses it as a key to retrieve the last used item for this file by this
+ * selection dialog. Returns null if nothing can be found, otherwise returns the string
+ * name of the item.
+ */
+ private String getLastUsedXmlName(UiElementNode ui_node) {
+ if (ui_node != null) {
+ AndroidEditor editor = ui_node.getEditor();
+ if (editor != null) {
+ IEditorInput editorInput = editor.getEditorInput();
+ if (editorInput instanceof FileEditorInput) {
+ IFile f = ((FileEditorInput) editorInput).getFile();
+ if (f != null) {
+ mLastUsedKey = f.getFullPath().toPortableString();
+
+ return sLastUsedXmlName.get(mLastUsedKey);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the last used item for this selection dialog for this file.
+ * @param objects The currently selected items. Only the first one is used if it is an
+ * {@link ElementDescriptor}.
+ */
+ private void setLastUsedXmlName(Object[] objects) {
+ if (mLastUsedKey != null &&
+ objects != null &&
+ objects.length > 0 &&
+ objects[0] instanceof ElementDescriptor) {
+ ElementDescriptor desc = (ElementDescriptor) objects[0];
+ sLastUsedXmlName.put(mLastUsedKey, desc.getXmlName());
+ }
+ }
+
+ /**
+ * Returns the most used sub-element name, if any, or null.
+ */
+ private String getMostUsedXmlName(UiElementNode ui_node) {
+ if (ui_node != null) {
+ TreeMap<String, Integer> counts = new TreeMap<String, Integer>();
+ int max = -1;
+
+ for (UiElementNode child : ui_node.getUiChildren()) {
+ String name = child.getDescriptor().getXmlName();
+ Integer i = counts.get(name);
+ int count = i == null ? 1 : i.intValue() + 1;
+ counts.put(name, count);
+ max = Math.max(max, count);
+ }
+
+ if (max > 0) {
+ // Find first key with this max and return it
+ for (Entry<String, Integer> entry : counts.entrySet()) {
+ if (entry.getValue().intValue() == max) {
+ return entry.getKey();
+ }
+ }
+ }
+ }
+ return null;
}
/**
@@ -126,6 +252,7 @@
@Override
protected void computeResult() {
setResult(Arrays.asList(getSelectedElements()));
+ setLastUsedXmlName(getSelectedElements());
}
/**
@@ -152,11 +279,47 @@
// Initialize the list state.
// This must be done after the filtered list as been created.
chooseNode(mChosenRootNode);
- setSelection(getInitialElementSelections().toArray());
+
+ // Set the initial selection
+ setInitialSelection(mChosenRootNode);
return contents;
}
/**
+ * Tries to set the initial selection based on the {@link #mInitialXmlName} computed
+ * in the constructor. The selection is only set if there's an element descriptor
+ * that matches the same exact XML name. When {@link #mInitialXmlName} starts with an
+ * asterisk, it means to do a partial case-insensitive match on the start of the
+ * strings.
+ */
+ private void setInitialSelection(UiElementNode rootNode) {
+ ElementDescriptor initialElement = null;
+
+ if (mInitialXmlName != null && mInitialXmlName.length() > 0) {
+ String name = mInitialXmlName;
+ boolean partial = name.startsWith("*"); //$NON-NLS-1$
+ if (partial) {
+ name = name.substring(1).toLowerCase();
+ }
+
+ for (ElementDescriptor desc : getAllowedDescriptors(rootNode)) {
+ if (!partial && desc.getXmlName().equals(name)) {
+ initialElement = desc;
+ break;
+ } else if (partial) {
+ String name2 = desc.getXmlLocalName().toLowerCase();
+ if (name.startsWith(name2) || name2.startsWith(name)) {
+ initialElement = desc;
+ break;
+ }
+ }
+ }
+ }
+
+ setSelection(initialElement == null ? null : new ElementDescriptor[] { initialElement });
+ }
+
+ /**
* Creates the message text widget and sets layout data.
* @param content the parent composite of the message area.
*/
@@ -217,13 +380,23 @@
*/
private void chooseNode(UiElementNode ui_node) {
mChosenRootNode = ui_node;
+ setListElements(getAllowedDescriptors(ui_node));
+ }
+ /**
+ * Returns the list of {@link ElementDescriptor}s that can be added to the given
+ * UI node.
+ *
+ * @param ui_node The UI node to which element should be added. Cannot be null.
+ * @return A non-null array of {@link ElementDescriptor}. The array might be empty.
+ */
+ private ElementDescriptor[] getAllowedDescriptors(UiElementNode ui_node) {
if (ui_node == mLocalRootNode &&
mDescriptorFilters != null &&
mDescriptorFilters.length != 0) {
- setListElements(mDescriptorFilters);
+ return mDescriptorFilters;
} else {
- setListElements(ui_node.getDescriptor().getChildren());
+ return ui_node.getDescriptor().getChildren();
}
}
}
diff --git a/tools/mkstubs/README.txt b/tools/mkstubs/README.txt
new file mode 100644
index 0000000..8f9abbc
--- /dev/null
+++ b/tools/mkstubs/README.txt
@@ -0,0 +1,90 @@
+2009/04/20.
+
+-------
+1- Goal
+-------
+
+MkStub is small tool that takes a given JAR and filters all the private stuff we don't want to
+expose, e.g.:
+- remove all private members.
+- only include a subset of classes.
+- exclude specific classes, fields or methods.
+
+Each method body is replaced by the bytecode for 'throw new RuntimeException("stub");'.
+
+
+--------
+2- Usage
+--------
+
+To control it, you give it patterns like this:
+
+ +foo => accepts all items which signature is exactly "foo"
+ +foo* => accepts all items which signature starts by "foo"
+ -bar => rejects all items which signature is exactly "bar"
+ -bar* => rejects all items which signature starts by "bar"
+
+Signatures are defined by:
+- a package name, e.g. com.android.blah
+- a dot followed by a class name
+- a # followed by a field or method name
+- an internal "Java method signature" that define parameters types and return value.
+
+Examples of signatures:
+ com.android.blah
+ com.android.blah.MyClass
+ com.android.blah.MyClass$MyInnerClass
+ com.android.blah.MyClass#mPrivateField
+ com.android.blah.MyClass#getInternalStuff
+ com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
+
+An example of configuration file:
+ +com.android.blah
+ -com.android.blah.MyClass$MyInnerClass
+ -com.android.blah.MyClass#mPrivateField
+ -com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
+
+This would include only the indicated package yet would totally exclude the inner class
+and the specific field and the method with the exact given signature.
+
+
+
+To invoke MkStub, the syntax is:
+
+ $ java -jar mkstubs input.jar output.jar [@configfile -pattern +pattern ...]
+
+
+
+--------------------
+3- Known Limitations
+--------------------
+
+Most of the following limitations exist solely because of the short development time and
+because the tool was designed to solve one task and not just to be super generic. That means
+any limitation here can be easily lifted.
+
+- The generated constructors are not proper. They do not invoke the matching super()
+ before the generated throw exception. Any attempt to load such a class should trigger
+ an error from the byte code verifier or the class loader.
+
+- We do not currently check whether a class or method uses only included types.
+ Suggestion: if type x.y.z is excluded, then any field, annotation, generic type,
+ method parameter or return value that uses that type should generate a fatal error.
+
+- We do not filter out private classes. Their .class will still be present in the
+ output (and stubbed), unless they are explicitly excluded.
+ This is not orthogonal to the fact that private fields and methods are automatically
+ excluded.
+
+- Private fields and methods are automatically excluded. There is no command line
+ switch to prevent that.
+
+- The stubbed source is always generated. For example if the output jar name is
+ given as ~/somedir/myfinal.jar, there will be a directory created at
+ ~/somedir/myfinal.jar_sources that will contain the equivalent Java sources.
+ There is not command line switch to prevent that.
+
+- There is no attempt to match features or behavior with DroidDoc.
+
+--
+end
diff --git a/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
index 0a37f29..5e64ae6 100644
--- a/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
@@ -28,7 +28,10 @@
import java.util.zip.ZipFile;
/**
- *
+ * Analyzes an input Jar to get all the relevant classes according to the given filter.
+ * <p/>
+ * This is mostly a helper extracted for convenience. Callers will want to use
+ * {@link #parseInputJar(String)} followed by {@link #filter(Map, Filter)}.
*/
class AsmAnalyzer {
@@ -66,12 +69,22 @@
}
}
- public void filter(Map<String, ClassReader> classes, Filter filter) {
+ /**
+ * Filters the set of classes. Removes all classes that should not be included in the
+ * filter or that should be excluded. This modifies the map in-place.
+ *
+ * @param classes The in-out map of classes to examine and filter. The map is filtered
+ * in-place.
+ * @param filter A filter describing which classes to include and which ones to exclude.
+ */
+ void filter(Map<String, ClassReader> classes, Filter filter) {
Set<String> keys = classes.keySet();
for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
String key = it.next();
+ // TODO: We *could* filter out all private classes here: classes.get(key).getAccess().
+
// remove if we don't keep it
if (!filter.accept(key)) {
System.out.println("- Remove class " + key);
diff --git a/tools/mkstubs/src/com/android/mkstubs/Filter.java b/tools/mkstubs/src/com/android/mkstubs/Filter.java
index c566c6b..0dcd8da 100644
--- a/tools/mkstubs/src/com/android/mkstubs/Filter.java
+++ b/tools/mkstubs/src/com/android/mkstubs/Filter.java
@@ -19,7 +19,15 @@
import java.util.TreeSet;
/**
- *
+ * A "filter" holds the various patterns that MkStubs should accept (include)
+ * or reject (exclude). Patterns can be of two kind:
+ * <ul>
+ * <li>Full patterns are simple string matches, similar to a "^pattern$" regex.
+ * <li>Prefix patterns are partial string matches, similar to a "^pattern.*" regex.
+ * </ul>
+ * <p/>
+ * The {@link #accept(String)} method examines a given string against the known
+ * pattern to decide if it should be included.
*/
class Filter {
private TreeSet<String> mIncludePrefix = new TreeSet<String>();
@@ -27,22 +35,44 @@
private TreeSet<String> mExcludePrefix = new TreeSet<String>();
private TreeSet<String> mExcludeFull = new TreeSet<String>();
+ /**
+ * Returns the set of all full patterns to be included.
+ */
public TreeSet<String> getIncludeFull() {
return mIncludeFull;
}
-
+
+ /**
+ * Returns the set of all prefix patterns to be included.
+ */
public TreeSet<String> getIncludePrefix() {
return mIncludePrefix;
}
+ /**
+ * Returns the set of all full patterns to be excluded.
+ */
public TreeSet<String> getExcludeFull() {
return mExcludeFull;
}
+ /**
+ * Returns the set of all prefix patterns to be excluded.
+ */
public TreeSet<String> getExcludePrefix() {
return mExcludePrefix;
}
-
+
+ /**
+ * Checks if the given string passes the various include/exclude rules.
+ * The matching is done as follows:
+ * <ul>
+ * <li> The string must match either a full include or a prefix include.
+ * <li> The string must not match any full exclude nor any prefix exclude.
+ * </ul>
+ * @param s The string to accept or reject.
+ * @return True if the string can be accepted, false if it must be rejected.
+ */
public boolean accept(String s) {
// Check if it can be included.
diff --git a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
index c3585a8..d3b06f7 100644
--- a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
+++ b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
@@ -25,7 +25,8 @@
import org.objectweb.asm.Opcodes;
/**
- * A class visitor that filters out all the referenced exclusions
+ * A class visitor that filters out all members (fields, methods and inner classes) that are
+ * either private or rejected by the {@link Filter}.
*/
class FilterClassAdapter extends ClassAdapter {
diff --git a/tools/mkstubs/src/com/android/mkstubs/Main.java b/tools/mkstubs/src/com/android/mkstubs/Main.java
index fd8fa4d..01cda71 100644
--- a/tools/mkstubs/src/com/android/mkstubs/Main.java
+++ b/tools/mkstubs/src/com/android/mkstubs/Main.java
@@ -26,10 +26,15 @@
/**
- *
+ * Main entry point of the MkStubs app.
+ * <p/>
+ * For workflow details, see {@link #process(Params)}.
*/
public class Main {
+ /**
+ * A struct-like class to hold the various input values (e.g. command-line args)
+ */
static class Params {
private String mInputJarPath;
private String mOutputJarPath;
@@ -40,22 +45,25 @@
mOutputJarPath = outputJarPath;
mFilter = new Filter();
}
-
+
+ /** Returns the name of the input jar, where to read classes from. */
public String getInputJarPath() {
return mInputJarPath;
}
+ /** Returns the name of the output jar, where to write classes to. */
public String getOutputJarPath() {
return mOutputJarPath;
}
-
+
+ /** Returns the current instance of the filter, the include/exclude patterns. */
public Filter getFilter() {
return mFilter;
}
}
/**
- * @param args
+ * Main entry point. Processes arguments then performs the "real" work.
*/
public static void main(String[] args) {
@@ -68,6 +76,17 @@
}
}
+ /**
+ * Grabs command-line arguments.
+ * The expected arguments are:
+ * <ul>
+ * <li> The filename of the input Jar.
+ * <li> The filename of the output Jar.
+ * <li> One or more include/exclude patterns or files containing these patterns.
+ * See {@link #addString(Params, String)} for syntax.
+ * </ul>
+ * @throws IOException on failure to read a pattern file.
+ */
private Params processArgs(String[] args) throws IOException {
if (args.length < 2) {
@@ -83,7 +102,26 @@
return p;
}
+ /**
+ * Adds one pattern string to the current filter.
+ * The syntax must be:
+ * <ul>
+ * <li> +full_include or +prefix_include*
+ * <li> -full_exclude or -prefix_exclude*
+ * <li> @filename
+ * </ul>
+ * The input string is trimmed so any space around the first letter (-/+/@) or
+ * at the end is removed. Empty strings are ignored.
+ *
+ * @param p The params which filters to edit.
+ * @param s The string to examine.
+ * @throws IOException
+ */
private void addString(Params p, String s) throws IOException {
+ if (s == null) {
+ return;
+ }
+
s = s.trim();
if (s.length() < 2) {
@@ -114,11 +152,20 @@
}
}
- private void addStringsFromFile(Params p, String inputFile)
+ /**
+ * Adds all the filter strings from the given file.
+ *
+ * @param p The params which filter to edit.
+ * @param osFilePath The OS path to the file containing the patterns.
+ * @throws IOException
+ *
+ * @see #addString(Params, String)
+ */
+ private void addStringsFromFile(Params p, String osFilePath)
throws IOException {
BufferedReader br = null;
try {
- br = new BufferedReader(new FileReader(inputFile));
+ br = new BufferedReader(new FileReader(osFilePath));
String line;
while ((line = br.readLine()) != null) {
addString(p, line);
@@ -128,6 +175,9 @@
}
}
+ /**
+ * Prints some help to stdout.
+ */
private void usage() {
System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]");
@@ -144,6 +194,17 @@
System.exit(1);
}
+ /**
+ * Performs the main workflow of this app:
+ * <ul>
+ * <li> Read the input Jar to get all its classes.
+ * <li> Filter out all classes that should not be included or that should be excluded.
+ * <li> Goes thru the classes, filters methods/fields and generate their source
+ * in a directory called "<outpath_jar_path>_sources"
+ * <li> Does the same filtering on the classes but this time generates the real stubbed
+ * output jar.
+ * </ul>
+ */
private void process(Params p) throws IOException {
AsmAnalyzer aa = new AsmAnalyzer();
Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
diff --git a/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
index 461a25f..f5a339d 100644
--- a/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
+++ b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
@@ -30,7 +30,11 @@
import java.util.Map.Entry;
/**
- *
+ * Given a set of already filtered classes, this filters out all private members and then
+ * generates the Java source for the remaining classes.
+ * <p/>
+ * This is an helper extracted for convenience. Callers just need to use
+ * {@link #generateSource(File, Map, Filter)}.
*/
class SourceGenerator {
diff --git a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
index 0321dc3..6126e17 100644
--- a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
+++ b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
@@ -32,7 +32,11 @@
import java.util.jar.JarOutputStream;
/**
- *
+ * Given a set of already filtered classes, this filters out all private members,
+ * stubs the remaining classes and then generates a Jar out of them.
+ * <p/>
+ * This is an helper extracted for convenience. Callers just need to use
+ * {@link #generateStubbedJar(File, Map, Filter)}.
*/
class StubGenerator {
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
index 757dcea..3b14ea7 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
@@ -19,7 +19,11 @@
import org.objectweb.asm.Opcodes;
/**
- *
+ * Source generator for the access fields of methods, fields and classes.
+ * <p/>
+ * Given an integer access field and a type ({@link #IS_CLASS}, {@link #IS_FIELD} or
+ * {@link #IS_METHOD}), the {@link #write(int, int)} method can generate a string
+ * desribing the access modifiers for a Java source.
*/
class AccessSourcer {
@@ -81,6 +85,9 @@
/**
* Generates a list of access keywords, e.g. "public final".
+ * <p/>
+ * It is up to the caller to filter extra keywords that should not be generated,
+ * e.g. {@link Flag#ACC_SYNTHETIC}.
*
* @param access The access mode, e.g. 33 or 18
* @param filter One of {@link #IS_CLASS}, {@link #IS_FIELD} or {@link #IS_METHOD}, which
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
index bbf1cb2..d2843a8 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
@@ -19,7 +19,7 @@
import org.objectweb.asm.AnnotationVisitor;
/**
- *
+ * An annotation visitor that generates Java source for an annotation.
*/
class AnnotationSourcer implements AnnotationVisitor {
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
index 189e1a0..3d95039 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
@@ -25,7 +25,7 @@
import org.objectweb.asm.signature.SignatureReader;
/**
- * A class visitor that rewrites a java source
+ * A class visitor that writes a java source.
*/
public class ClassSourcer implements ClassVisitor {
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
index 4f9c229..7f30a24 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
@@ -19,10 +19,11 @@
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader;
/**
- *
+ * A field visitor that generates Java source defining a field.
*/
class FieldSourcer implements FieldVisitor {
@@ -56,7 +57,7 @@
as.write(mAccess, AccessSourcer.IS_FIELD);
if (mSignature == null) {
- mOutput.write(" %s", mOutput.decodeDesc(mDesc));
+ mOutput.write(" %s", Type.getType(mDesc).getClassName());
} else {
mOutput.write(" ");
SignatureReader sigReader = new SignatureReader(mSignature);
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
index d474e4b..f58de32 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
@@ -26,7 +26,7 @@
import java.util.ArrayList;
/**
- *
+ * A method visitor that generates the Java source for a whole method.
*/
class MethodSourcer implements MethodVisitor {
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
index 837cf95..bc2218f 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
@@ -16,22 +16,38 @@
package com.android.mkstubs.sourcer;
-import org.objectweb.asm.Type;
-
import java.io.IOException;
import java.io.Writer;
/**
- *
+ * An {@link Output} objects is an helper to write to a character stream {@link Writer}.
+ * <p/>
+ * It provide some helper methods to the various "sourcer" classes from this package
+ * to help them write to the underlying stream.
*/
public class Output {
private final Writer mWriter;
+ /**
+ * Creates a new {@link Output} object that wraps the given {@link Writer}.
+ * <p/>
+ * The caller is responsible of opening and closing the {@link Writer}.
+ *
+ * @param writer The writer to write to. Could be a file, a string, etc.
+ */
public Output(Writer writer) {
mWriter = writer;
}
+ /**
+ * Writes a formatted string to the writer.
+ *
+ * @param format The format string.
+ * @param args The arguments for the format string.
+ *
+ * @see String#format(String, Object...)
+ */
public void write(String format, Object... args) {
try {
mWriter.write(String.format(format, args));
@@ -40,18 +56,21 @@
}
}
+ /**
+ * Writes a single character to the writer.
+ *
+ * @param c The character to write.
+ */
public void write(char c) {
write(Character.toString(c));
}
+ /**
+ * Writes a {@link StringBuilder} to the writer.
+ *
+ * @param sb The {@link StringBuilder#toString()} method is used to ge the string to write.
+ */
public void write(StringBuilder sb) {
write(sb.toString());
}
-
-
- public String decodeDesc(String desc) {
- return Type.getType(desc).getClassName();
- }
-
-
}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
index 6202528..7805d7d 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
@@ -24,14 +24,22 @@
import java.util.ArrayList;
/**
- * Note: most of the implementation is a duplicate of
- * ASM's SignatureWriter with some slight variations.
+ * A signature visitor that can be used to generate Java source corresponding to
+ * various types of signatures.
* <p/>
- * Note: When processing a method's signature, the signature order is the
- * reverse of the source order, e.g. it is (parameters)return-type where
- * we want to generate "return-type method-name (parameters)".
- * So in this case the return-type and parameters are not output directly
- * but are instead accumulated in internal variables.
+ * Terminology: a "signature" is a type descriptor for generics. There are different types
+ * of signatures depending on the context where they are used, e.g. method declarations,
+ * method parameters, class declarations, etc..
+ * <p/>
+ * Note: most of the implementation is a duplicate of ASM's SignatureWriter with some
+ * slight variations.
+ * <p/>
+ * Note: When processing a method's signature, the signature order is the reverse of the source
+ * order, e.g. the signature is written as "(parameters)return-type" where we want to generate
+ * "return-type method-name (parameters)". To hanlde this case, the return-type and parameters
+ * are <em>not</em> output directly but are instead accumulated in internal variables that you can
+ * get later using {@link #getReturnType()}, {@link #getParameters()}, {@link #getSuperClass()}
+ * and {@link #formalsToString()}.
*/
class SignatureSourcer implements SignatureVisitor {
@@ -58,10 +66,22 @@
*/
private int mArgumentStack;
+ /**
+ * {@link SignatureSourcer} generated when parsing the return type of <em>this</em>
+ * signature. Initially null.
+ */
private SignatureSourcer mReturnType;
+ /**
+ * {@link SignatureSourcer} generated when parsing the super class of <em>this</em>
+ * signature. Initially null.
+ */
private SignatureSourcer mSuperClass;
+ /**
+ * {@link SignatureSourcer}s for each parameters generated when parsing the method parameters
+ * of <em>this</em> signature. Initially empty but not null.
+ */
private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>();
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
index dea0a52..8f9ae11 100644
--- a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
@@ -24,7 +24,8 @@
import org.objectweb.asm.MethodVisitor;
/**
- *
+ * A class visitor that generates stubs for all methods of the visited class.
+ * Everything else is passed as-is.
*/
public class ClassStubber extends ClassAdapter {
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
index 3e200cd..1617809 100644
--- a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
@@ -24,7 +24,13 @@
import org.objectweb.asm.Opcodes;
/**
- *
+ * A method visitor that generates a code stub for the visited method.
+ * <p/>
+ * Annotations and parameters are passed as-is.
+ * All other code is replaced by the following:
+ * <pre>throw new RuntimeException("stub");</pre>
+ * Note that constructors rewritten this way will probably fail with the runtime bytecode
+ * verifier since no call to <code>super</code> is generated.
*/
public class MethodStubber extends MethodAdapter {
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index 003021c..236327c 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -64,7 +64,7 @@
<property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
<!-- Tools -->
- <condition property="exe" value="exe" else=""><os family="windows"/></condition>
+ <condition property="exe" value=".exe" else=""><os family="windows"/></condition>
<property name="adb" value="${android-tools}/adb${exe}"/>
<!-- rules -->
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 9f3fb99..c20bfa4 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -482,22 +482,29 @@
if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
String value = "";
- if (arg.getDefaultValue() instanceof String[]) {
- for (String v : (String[]) arg.getDefaultValue()) {
- if (value.length() > 0) {
- value += ", ";
+ String required = "";
+ if (arg.isMandatory()) {
+ required = " [required]";
+
+ } else {
+ if (arg.getDefaultValue() instanceof String[]) {
+ for (String v : (String[]) arg.getDefaultValue()) {
+ if (value.length() > 0) {
+ value += ", ";
+ }
+ value += v;
}
- value += v;
+ } else if (arg.getDefaultValue() != null) {
+ Object v = arg.getDefaultValue();
+ if (arg.getMode() != MODE.BOOLEAN || v.equals(Boolean.TRUE)) {
+ value = v.toString();
+ }
}
- } else if (arg.getDefaultValue() != null) {
- value = arg.getDefaultValue().toString();
- }
- if (value.length() > 0) {
- value = " (" + value + ")";
+ if (value.length() > 0) {
+ value = " [Default: " + value + "]";
+ }
}
- String required = arg.isMandatory() ? " [required]" : "";
-
stdout(" -%1$s %2$-10s %3$s%4$s%5$s",
arg.getShortArg(),
"--" + arg.getLongArg(),
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 66c2419..36172e9 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -138,9 +138,13 @@
// --- create project ---
+ /* Disabled for ADT 0.9 / Cupcake SDK 1.5_r1 release. [bug #1795718].
+ This currently does not work, the alias build rules need to be fixed.
+
define(MODE.ENUM, true,
VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE,
"Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
+ */
define(MODE.STRING, true,
VERB_CREATE, OBJECT_PROJECT,
"p", KEY_PATH,
diff --git a/tools/yuv420sp2rgb/Android.mk b/tools/yuv420sp2rgb/Android.mk
new file mode 100644
index 0000000..b7e7802
--- /dev/null
+++ b/tools/yuv420sp2rgb/Android.mk
@@ -0,0 +1,16 @@
+# Copyright 2005 Google Inc. All Rights Reserved.
+#
+# Android.mk for yuv420sp2rgb
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := yuv420sp2rgb.c cmdline.c debug.c
+
+LOCAL_MODULE := yuv420sp2rgb
+
+include $(BUILD_HOST_EXECUTABLE)
+endif
diff --git a/tools/yuv420sp2rgb/cmdline.c b/tools/yuv420sp2rgb/cmdline.c
new file mode 100644
index 0000000..eb870b5
--- /dev/null
+++ b/tools/yuv420sp2rgb/cmdline.c
@@ -0,0 +1,147 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] = {
+ {"output", required_argument, 0, 'o'},
+ {"height", required_argument, 0, 'h'},
+ {"width", required_argument, 0, 'w'},
+ {"gray", no_argument, 0, 'g'},
+ {"type", required_argument, 0, 't'},
+ {"rotate", required_argument, 0, 'r'},
+ {"verbose", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 1},
+ {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static const char *descriptions[] = {
+ "output file",
+ "image height in pixels",
+ "image width in pixels",
+ "process the luma plane only",
+ "encode as one of { 'ppm', 'rgb', or 'argb' }",
+ "rotate (90, -90, 180 degrees)",
+ "print verbose output",
+ "print this help screen",
+};
+
+void print_help(const char *name) {
+ fprintf(stdout,
+ "Converts yuv 4:2:0 to rgb24 and generates a PPM file.\n"
+ "invokation:\n"
+ "\t%s infile --height <height> --width <width> --output <outfile> -t <ppm|grb|argb> [ --gray ] [ --rotate <degrees> ] [ --verbose ]\n"
+ "\t%s infile --help\n",
+ name, name);
+ fprintf(stdout, "options:\n");
+ struct option *opt = long_options;
+ const char **desc = descriptions;
+ while (opt->name) {
+ fprintf(stdout, "\t-%c/--%s%s: %s\n",
+ isprint(opt->val) ? opt->val : ' ',
+ opt->name,
+ (opt->has_arg ? " (argument)" : ""),
+ *desc);
+ opt++;
+ desc++;
+ }
+}
+
+int get_options(int argc, char **argv,
+ char **outfile,
+ int *height,
+ int *width,
+ int *gray,
+ char **type,
+ int *rotate,
+ int *verbose) {
+ int c;
+
+ ASSERT(outfile); *outfile = NULL;
+ ASSERT(height); *height = -1;
+ ASSERT(width); *width = -1;
+ ASSERT(gray); *gray = 0;
+ ASSERT(rotate); *rotate = 0;
+ ASSERT(verbose); *verbose = 0;
+ ASSERT(type); *type = NULL;
+
+ while (1) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv,
+ "Vgo:h:w:r:t:",
+ long_options,
+ &option_index);
+ /* Detect the end of the options. */
+ if (c == -1) break;
+
+ if (isgraph(c)) {
+ INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+ }
+
+#define SET_STRING_OPTION(name) do { \
+ ASSERT(optarg); \
+ (*name) = strdup(optarg); \
+} while(0)
+
+#define SET_INT_OPTION(val) do { \
+ ASSERT(optarg); \
+ if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \
+ FAILIF(1 != sscanf(optarg+2, "%x", val), \
+ "Expecting a hexadecimal argument!\n"); \
+ } else { \
+ FAILIF(1 != sscanf(optarg, "%d", val), \
+ "Expecting a decimal argument!\n"); \
+ } \
+} while(0)
+
+ switch (c) {
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ if (long_options[option_index].flag != 0)
+ break;
+ INFO ("option %s", long_options[option_index].name);
+ if (optarg)
+ INFO (" with arg %s", optarg);
+ INFO ("\n");
+ break;
+ case 1: print_help(argv[0]); exit(1); break;
+ case 'o':
+ SET_STRING_OPTION(outfile);
+ break;
+ case 't':
+ SET_STRING_OPTION(type);
+ break;
+ case 'h':
+ SET_INT_OPTION(height);
+ break;
+ case 'w':
+ SET_INT_OPTION(width);
+ break;
+ case 'r':
+ SET_INT_OPTION(rotate);
+ break;
+ case 'g': *gray = 1; break;
+ case 'V': *verbose = 1; break;
+ case '?':
+ /* getopt_long already printed an error message. */
+ break;
+
+#undef SET_STRING_OPTION
+#undef SET_INT_OPTION
+
+ default:
+ FAILIF(1, "Unknown option");
+ }
+ }
+
+ return optind;
+}
diff --git a/tools/yuv420sp2rgb/cmdline.h b/tools/yuv420sp2rgb/cmdline.h
new file mode 100644
index 0000000..49760ad
--- /dev/null
+++ b/tools/yuv420sp2rgb/cmdline.h
@@ -0,0 +1,15 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(const char *executable_name);
+
+extern int get_options(int argc, char **argv,
+ char **outfile,
+ int *height,
+ int *width,
+ int *gray,
+ char **type,
+ int *rotate,
+ int *verbose);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/yuv420sp2rgb/debug.c b/tools/yuv420sp2rgb/debug.c
new file mode 100644
index 0000000..263e09f
--- /dev/null
+++ b/tools/yuv420sp2rgb/debug.c
@@ -0,0 +1,38 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ fprintf(s, "%p: ", b);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ } else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n%p: ", i, b);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
+
diff --git a/tools/yuv420sp2rgb/debug.h b/tools/yuv420sp2rgb/debug.h
new file mode 100644
index 0000000..9bbf47f
--- /dev/null
+++ b/tools/yuv420sp2rgb/debug.h
@@ -0,0 +1,90 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR PRINT
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/yuv420sp2rgb/yuv420sp2rgb.c b/tools/yuv420sp2rgb/yuv420sp2rgb.c
new file mode 100644
index 0000000..6fe82e5
--- /dev/null
+++ b/tools/yuv420sp2rgb/yuv420sp2rgb.c
@@ -0,0 +1,339 @@
+#include <stdio.h>
+#include <debug.h>
+#include <cmdline.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#ifndef max
+#define max(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
+#define min(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
+#endif
+
+#define CONVERT_TYPE_PPM 0
+#define CONVERT_TYPE_RGB 1
+#define CONVERT_TYPE_ARGB 2
+
+/*
+ YUV 4:2:0 image with a plane of 8 bit Y samples followed by an interleaved
+ U/V plane containing 8 bit 2x2 subsampled chroma samples.
+ except the interleave order of U and V is reversed.
+
+ H V
+ Y Sample Period 1 1
+ U (Cb) Sample Period 2 2
+ V (Cr) Sample Period 2 2
+ */
+
+typedef struct rgb_context {
+ unsigned char *buffer;
+ int width;
+ int height;
+ int rotate;
+ int i;
+ int j;
+ int size; /* for debugging */
+} rgb_context;
+
+typedef void (*rgb_cb)(
+ unsigned char r,
+ unsigned char g,
+ unsigned char b,
+ rgb_context *ctx);
+
+const int bytes_per_pixel = 2;
+
+static void color_convert_common(
+ unsigned char *pY, unsigned char *pUV,
+ int width, int height,
+ unsigned char *buffer,
+ int size, /* buffer size in bytes */
+ int gray,
+ int rotate,
+ rgb_cb cb)
+{
+ int i, j;
+ int nR, nG, nB;
+ int nY, nU, nV;
+ rgb_context ctx;
+
+ ctx.buffer = buffer;
+ ctx.size = size; /* debug */
+ ctx.width = width;
+ ctx.height = height;
+ ctx.rotate = rotate;
+
+ if (gray) {
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ nB = *(pY + i * width + j);
+ ctx.i = i;
+ ctx.j = j;
+ cb(nB, nB, nB, &ctx);
+ }
+ }
+ } else {
+ // YUV 4:2:0
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ nY = *(pY + i * width + j);
+ nV = *(pUV + (i/2) * width + bytes_per_pixel * (j/2));
+ nU = *(pUV + (i/2) * width + bytes_per_pixel * (j/2) + 1);
+
+ // Yuv Convert
+ nY -= 16;
+ nU -= 128;
+ nV -= 128;
+
+ if (nY < 0)
+ nY = 0;
+
+ // nR = (int)(1.164 * nY + 2.018 * nU);
+ // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
+ // nB = (int)(1.164 * nY + 1.596 * nV);
+
+ nB = (int)(1192 * nY + 2066 * nU);
+ nG = (int)(1192 * nY - 833 * nV - 400 * nU);
+ nR = (int)(1192 * nY + 1634 * nV);
+
+ nR = min(262143, max(0, nR));
+ nG = min(262143, max(0, nG));
+ nB = min(262143, max(0, nB));
+
+ nR >>= 10; nR &= 0xff;
+ nG >>= 10; nG &= 0xff;
+ nB >>= 10; nB &= 0xff;
+
+ ctx.i = i;
+ ctx.j = j;
+ cb(nR, nG, nB, &ctx);
+ }
+ }
+ }
+}
+
+static void rgb16_cb(
+ unsigned char r,
+ unsigned char g,
+ unsigned char b,
+ rgb_context *ctx)
+{
+ unsigned short *rgb16 = (unsigned short *)ctx->buffer;
+ *(rgb16 + ctx->i * ctx->width + ctx->j) = b | (g << 5) | (r << 11);
+}
+
+static void common_rgb_cb(
+ unsigned char r,
+ unsigned char g,
+ unsigned char b,
+ rgb_context *ctx,
+ int alpha)
+{
+ unsigned char *out = ctx->buffer;
+ int offset = 0;
+ int bpp;
+ int i = 0;
+ switch(ctx->rotate) {
+ case 0: /* no rotation */
+ offset = ctx->i * ctx->width + ctx->j;
+ break;
+ case 1: /* 90 degrees */
+ offset = ctx->height * (ctx->j + 1) - ctx->i;
+ break;
+ case 2: /* 180 degrees */
+ offset = (ctx->height - 1 - ctx->i) * ctx->width + ctx->j;
+ break;
+ case 3: /* 270 degrees */
+ offset = (ctx->width - 1 - ctx->j) * ctx->height + ctx->i;
+ break;
+ default:
+ FAILIF(1, "Unexpected roation value %d!\n", ctx->rotate);
+ }
+
+ bpp = 3 + !!alpha;
+ offset *= bpp;
+ FAILIF(offset < 0, "point (%d, %d) generates a negative offset.\n", ctx->i, ctx->j);
+ FAILIF(offset + bpp > ctx->size, "point (%d, %d) at offset %d exceeds the size %d of the buffer.\n",
+ ctx->i, ctx->j,
+ offset,
+ ctx->size);
+
+ out += offset;
+
+ if (alpha) out[i++] = 0xff;
+ out[i++] = r;
+ out[i++] = g;
+ out[i] = b;
+}
+
+static void rgb24_cb(
+ unsigned char r,
+ unsigned char g,
+ unsigned char b,
+ rgb_context *ctx)
+{
+ return common_rgb_cb(r,g,b,ctx,0);
+}
+
+static void argb_cb(
+ unsigned char r,
+ unsigned char g,
+ unsigned char b,
+ rgb_context *ctx)
+{
+ return common_rgb_cb(r,g,b,ctx,1);
+}
+
+static void convert(const char *infile,
+ const char *outfile,
+ int height,
+ int width,
+ int gray,
+ int type,
+ int rotate)
+{
+ void *in, *out;
+ int ifd, ofd, rc;
+ int psz = getpagesize();
+ static char header[1024];
+ int header_size;
+ size_t outsize;
+
+ int bpp = 3;
+ switch (type) {
+ case CONVERT_TYPE_PPM:
+ PRINT("encoding PPM\n");
+ if (rotate & 1)
+ header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", height, width);
+ else
+ header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", width, height);
+ break;
+ case CONVERT_TYPE_RGB:
+ PRINT("encoding raw RGB24\n");
+ header_size = 0;
+ break;
+ case CONVERT_TYPE_ARGB:
+ PRINT("encoding raw ARGB\n");
+ header_size = 0;
+ bpp = 4;
+ break;
+ }
+
+ outsize = header_size + width * height * bpp;
+ outsize = (outsize + psz - 1) & ~(psz - 1);
+
+ INFO("Opening input file %s\n", infile);
+ ifd = open(infile, O_RDONLY);
+ FAILIF(ifd < 0, "open(%s) failed: %s (%d)\n",
+ infile, strerror(errno), errno);
+
+ INFO("Opening output file %s\n", outfile);
+ ofd = open(outfile, O_RDWR | O_CREAT, 0664);
+ FAILIF(ofd < 0, "open(%s) failed: %s (%d)\n",
+ outfile, strerror(errno), errno);
+
+ INFO("Memory-mapping input file %s\n", infile);
+ in = mmap(0, width * height * 3 / 2, PROT_READ, MAP_PRIVATE, ifd, 0);
+ FAILIF(in == MAP_FAILED, "could not mmap input file: %s (%d)\n",
+ strerror(errno), errno);
+
+ INFO("Truncating output file %s to %d bytes\n", outfile, outsize);
+ FAILIF(ftruncate(ofd, outsize) < 0,
+ "Could not truncate output file to required size: %s (%d)\n",
+ strerror(errno), errno);
+
+ INFO("Memory mapping output file %s\n", outfile);
+ out = mmap(0, outsize, PROT_WRITE, MAP_SHARED, ofd, 0);
+ FAILIF(out == MAP_FAILED, "could not mmap output file: %s (%d)\n",
+ strerror(errno), errno);
+
+ INFO("PPM header (%d) bytes:\n%s\n", header_size, header);
+ FAILIF(write(ofd, header, header_size) != header_size,
+ "Error wrinting PPM header: %s (%d)\n",
+ strerror(errno), errno);
+
+ INFO("Converting %dx%d YUV 4:2:0 to RGB24...\n", width, height);
+ color_convert_common(in, in + width * height,
+ width, height,
+ out + header_size, outsize - header_size,
+ gray, rotate,
+ type == CONVERT_TYPE_ARGB ? argb_cb : rgb24_cb);
+}
+
+int verbose_flag;
+int quiet_flag;
+
+int main(int argc, char **argv) {
+
+ char *infile, *outfile, *type;
+ int height, width, gray, rotate;
+ int cmdline_error = 0;
+
+ /* Parse command-line arguments. */
+
+ int first = get_options(argc, argv,
+ &outfile,
+ &height,
+ &width,
+ &gray,
+ &type,
+ &rotate,
+ &verbose_flag);
+
+ if (first == argc) {
+ ERROR("You must specify an input file!\n");
+ cmdline_error++;
+ }
+ if (!outfile) {
+ ERROR("You must specify an output file!\n");
+ cmdline_error++;
+ }
+ if (height < 0 || width < 0) {
+ ERROR("You must specify both image height and width!\n");
+ cmdline_error++;
+ }
+
+ FAILIF(rotate % 90, "Rotation angle must be a multiple of 90 degrees!\n");
+
+ rotate /= 90;
+ rotate %= 4;
+ if (rotate < 0) rotate += 4;
+
+ if (cmdline_error) {
+ print_help(argv[0]);
+ exit(1);
+ }
+
+ infile = argv[first];
+
+ INFO("input file: [%s]\n", infile);
+ INFO("output file: [%s]\n", outfile);
+ INFO("height: %d\n", height);
+ INFO("width: %d\n", width);
+ INFO("gray only: %d\n", gray);
+ INFO("encode as: %s\n", type);
+ INFO("rotation: %d\n", rotate);
+
+ /* Convert the image */
+
+ int conv_type;
+ if (!type || !strcmp(type, "ppm"))
+ conv_type = CONVERT_TYPE_PPM;
+ else if (!strcmp(type, "rgb"))
+ conv_type = CONVERT_TYPE_RGB;
+ else if (!strcmp(type, "argb"))
+ conv_type = CONVERT_TYPE_ARGB;
+ else FAILIF(1, "Unknown encoding type %s.\n", type);
+
+ convert(infile, outfile,
+ height, width, gray,
+ conv_type,
+ rotate);
+
+ free(outfile);
+ return 0;
+}