OSDN Git Service

Merge "Remove some unused wifi dhcp code."
authorRobert Greenwalt <rgreenwalt@google.com>
Tue, 15 Feb 2011 22:26:37 +0000 (14:26 -0800)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 15 Feb 2011 22:26:37 +0000 (14:26 -0800)
65 files changed:
Android.mk
api/current.xml
core/java/android/animation/FloatKeyframeSet.java
core/java/android/animation/IntKeyframeSet.java
core/java/android/hardware/SensorManager.java
core/java/android/server/BluetoothInputProfileHandler.java [new file with mode: 0644]
core/java/android/server/BluetoothPanProfileHandler.java [new file with mode: 0644]
core/java/android/server/BluetoothService.java
core/java/android/text/Layout.java
core/java/android/view/View.java
core/java/android/view/ViewConfiguration.java
core/java/android/view/ViewGroup.java
core/java/android/view/inputmethod/InputMethodSubtype.java
core/java/android/webkit/WebView.java
core/java/android/webkit/ZoomManager.java
core/java/android/widget/AbsListView.java
core/java/android/widget/ListPopupWindow.java
core/java/android/widget/ListView.java
core/java/android/widget/SearchView.java
core/java/android/widget/TextView.java
core/java/com/android/internal/view/menu/MenuPopupHelper.java
core/jni/android_server_BluetoothService.cpp
core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png [new file with mode: 0644]
core/res/res/drawable-mdpi/text_cursor_holo_light.9.png [new file with mode: 0644]
core/res/res/values/attrs.xml
core/res/res/values/public.xml
core/res/res/values/styles.xml
core/res/res/values/themes.xml
docs/html/guide/developing/debugging/debugging-ui.jd
docs/html/images/developing/hv_device_window.png [new file with mode: 0644]
docs/html/images/developing/hv_pixelperfect.png [new file with mode: 0644]
docs/html/images/developing/hv_treeview_screenshot.png [new file with mode: 0644]
docs/html/images/developing/hv_view_hierarchy_window.png [new file with mode: 0644]
docs/html/resources/resources-data.js
docs/html/sdk/android-3.0.jd
graphics/java/android/graphics/Bitmap.java
media/java/android/media/videoeditor/MediaArtistNativeHelper.java
media/libstagefright/AwesomePlayer.cpp
media/libstagefright/include/AwesomePlayer.h
media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
media/libstagefright/rtsp/APacketSource.cpp
media/libstagefright/rtsp/ARTPConnection.cpp
media/libstagefright/rtsp/ARTPSource.cpp
media/libstagefright/rtsp/ARTSPController.cpp
media/libstagefright/rtsp/ASessionDescription.cpp
media/libstagefright/rtsp/MyHandler.h
media/mtp/MtpDevice.cpp
media/tests/MediaFrameworkTest/res/layout/surface_view.xml
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java [new file with mode: 0755]
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java [new file with mode: 0755]
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java [new file with mode: 0755]
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java [new file with mode: 0644]
media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 [new file with mode: 0755]
media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png [changed mode: 0644->0755]
media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp [deleted file]
media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp [new file with mode: 0644]
packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
services/java/com/android/server/InputMethodManagerService.java
services/java/com/android/server/WindowManagerService.java
telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java

index ebc5213..1407631 100644 (file)
@@ -429,13 +429,13 @@ web_docs_sample_code_flags := \
                -samplecode $(sample_dir)/SpinnerTest \
                            resources/samples/SpinnerTest "SpinnerTest" \
                -samplecode $(sample_dir)/StackWidget \
-                           resources/samples/StackWidget "StackWidget" \
+                           resources/samples/StackWidget "StackView Widget" \
                -samplecode $(sample_dir)/TicTacToeLib  \
                            resources/samples/TicTacToeLib "TicTacToeLib" \
                -samplecode $(sample_dir)/TicTacToeMain \
                            resources/samples/TicTacToeMain "TicTacToeMain" \
                -samplecode $(sample_dir)/WeatherListWidget \
-                           resources/samples/WeatherListWidget "Weather List Widget Sample" \
+                           resources/samples/WeatherListWidget "Weather List Widget" \
                -samplecode $(sample_dir)/Wiktionary \
                            resources/samples/Wiktionary "Wiktionary" \
                -samplecode $(sample_dir)/WiktionarySimple \
index 968b8ed..7806c24 100644 (file)
  visibility="public"
 >
 </method>
-<field name="DRAG_FLAG_GLOBAL"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="DRAWING_CACHE_QUALITY_AUTO"
  type="int"
  transient="false"
 >
 <implements name="android.os.Parcelable">
 </implements>
+<method name="containsExtraValueKey"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="s" type="java.lang.String">
+</parameter>
+</method>
 <method name="describeContents"
  return="int"
  abstract="false"
  visibility="public"
 >
 </method>
+<method name="getExtraValueOf"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="s" type="java.lang.String">
+</parameter>
+</method>
 <method name="getIconResId"
  return="int"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
index 4009f13..377b5a0 100644 (file)
@@ -87,7 +87,7 @@ class FloatKeyframeSet extends KeyframeSet {
             }
             float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
             return mEvaluator == null ?
-                    prevValue + fraction * (nextValue - prevValue) :
+                    prevValue + intervalFraction * (nextValue - prevValue) :
                     ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                             floatValue();
         } else if (fraction >= 1f) {
@@ -103,7 +103,7 @@ class FloatKeyframeSet extends KeyframeSet {
             }
             float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
             return mEvaluator == null ?
-                    prevValue + fraction * (nextValue - prevValue) :
+                    prevValue + intervalFraction * (nextValue - prevValue) :
                     ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                             floatValue();
         }
@@ -120,7 +120,7 @@ class FloatKeyframeSet extends KeyframeSet {
                 float prevValue = prevKeyframe.getFloatValue();
                 float nextValue = nextKeyframe.getFloatValue();
                 return mEvaluator == null ?
-                        prevValue + fraction * (nextValue - prevValue) :
+                        prevValue + intervalFraction * (nextValue - prevValue) :
                         ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                             floatValue();
             }
index 5629c5e..7b7c876 100644 (file)
@@ -87,7 +87,7 @@ class IntKeyframeSet extends KeyframeSet {
             }
             float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
             return mEvaluator == null ?
-                    prevValue + (int)(fraction * (nextValue - prevValue)) :
+                    prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
                     ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                             intValue();
         } else if (fraction >= 1f) {
@@ -103,7 +103,7 @@ class IntKeyframeSet extends KeyframeSet {
             }
             float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
             return mEvaluator == null ?
-                    prevValue + (int)(fraction * (nextValue - prevValue)) :
+                    prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
                     ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
         }
         IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
@@ -119,7 +119,7 @@ class IntKeyframeSet extends KeyframeSet {
                 int prevValue = prevKeyframe.getIntValue();
                 int nextValue = nextKeyframe.getIntValue();
                 return mEvaluator == null ?
-                        prevValue + (int)(fraction * (nextValue - prevValue)) :
+                        prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
                         ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                                 intValue();
             }
index f079e42..dd4096b 100644 (file)
@@ -380,6 +380,58 @@ public class SensorManager
 
     /*-----------------------------------------------------------------------*/
 
+    private class SensorEventPool {
+        private final int mPoolSize;
+        private final SensorEvent mPool[];
+        private int mNumItemsInPool;
+
+        private SensorEvent createSensorEvent() {
+            // maximal size for all legacy events is 3
+            return new SensorEvent(3);
+        }
+
+        SensorEventPool(int poolSize) {
+            mPoolSize = poolSize;
+            mNumItemsInPool = poolSize;
+            mPool = new SensorEvent[poolSize];
+        }
+
+        SensorEvent getFromPool() {
+            SensorEvent t = null;
+            synchronized (this) {
+                if (mNumItemsInPool > 0) {
+                    // remove the "top" item from the pool
+                    final int index = mPoolSize - mNumItemsInPool;
+                    t = mPool[index];
+                    mPool[index] = null;
+                    mNumItemsInPool--;
+                }
+            }
+            if (t == null) {
+                // the pool was empty or this item was removed from the pool for
+                // the first time. In any case, we need to create a new item.
+                t = createSensorEvent();
+            }
+            return t;
+        }
+
+        void returnToPool(SensorEvent t) {
+            synchronized (this) {
+                // is there space left in the pool?
+                if (mNumItemsInPool < mPoolSize) {
+                    // if so, return the item to the pool
+                    mNumItemsInPool++;
+                    final int index = mPoolSize - mNumItemsInPool;
+                    mPool[index] = t;
+                }
+            }
+        }
+    }
+
+    private static SensorEventPool sPool;
+
+    /*-----------------------------------------------------------------------*/
+
     static private class SensorThread {
 
         Thread mThread;
@@ -485,10 +537,9 @@ public class SensorManager
     /*-----------------------------------------------------------------------*/
 
     private class ListenerDelegate {
-        final SensorEventListener mSensorEventListener;
+        private final SensorEventListener mSensorEventListener;
         private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
         private final Handler mHandler;
-        private SensorEvent mValuesPool;
         public SparseBooleanArray mSensors = new SparseBooleanArray();
         public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
         public SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -527,40 +578,12 @@ public class SensorManager
                     }
 
                     mSensorEventListener.onSensorChanged(t);
-                    returnToPool(t);
+                    sPool.returnToPool(t);
                 }
             };
             addSensor(sensor);
         }
 
-        protected SensorEvent createSensorEvent() {
-            // maximal size for all legacy events is 3
-            return new SensorEvent(3);
-        }
-
-        protected SensorEvent getFromPool() {
-            SensorEvent t = null;
-            synchronized (this) {
-                // remove the array from the pool
-                t = mValuesPool;
-                mValuesPool = null;
-            }
-            if (t == null) {
-                // the pool was empty, we need a new one
-                t = createSensorEvent();
-            }
-            return t;
-        }
-
-        protected void returnToPool(SensorEvent t) {
-            synchronized (this) {
-                // put back the array into the pool
-                if (mValuesPool == null) {
-                    mValuesPool = t;
-                }
-            }
-        }
-
         Object getListener() {
             return mSensorEventListener;
         }
@@ -582,7 +605,7 @@ public class SensorManager
         }
 
         void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
-            SensorEvent t = getFromPool();
+            SensorEvent t = sPool.getFromPool();
             final float[] v = t.values;
             v[0] = values[0];
             v[1] = values[1];
@@ -644,6 +667,7 @@ public class SensorManager
                     }
                 } while (i>0);
 
+                sPool = new SensorEventPool( sFullSensorsList.size()*2 );
                 sSensorThread = new SensorThread();
             }
         }
@@ -1970,7 +1994,8 @@ public class SensorManager
         if (rotationVector.length == 4) {
             q0 = rotationVector[3];
         } else {
-            q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3);
+            q0 = 1 - q1*q1 - q2*q2 - q3*q3;
+            q0 = (q0 > 0) ? (float)Math.sqrt(q0) : 0;
         }
 
         float sq_q1 = 2 * q1 * q1;
diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java
new file mode 100644 (file)
index 0000000..7ffa5ae
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2011 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 android.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothProfileState;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This handles all the operations on the HID profile.
+ * All functions are called by BluetoothService, as Bluetooth Service
+ * is the Service handler for the HID profile.
+ */
+final class BluetoothInputProfileHandler {
+    private static final String TAG = "BluetoothInputProfileHandler";
+    private static final boolean DBG = true;
+
+    public static BluetoothInputProfileHandler sInstance;
+    private Context mContext;
+    private BluetoothService mBluetoothService;
+    private final HashMap<BluetoothDevice, Integer> mInputDevices;
+    private final BluetoothProfileState mHidProfileState;
+
+    private BluetoothInputProfileHandler(Context context, BluetoothService service) {
+        mContext = context;
+        mBluetoothService = service;
+        mInputDevices = new HashMap<BluetoothDevice, Integer>();
+        mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
+        mHidProfileState.start();
+    }
+
+    static synchronized BluetoothInputProfileHandler getInstance(Context context,
+            BluetoothService service) {
+        if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service);
+        return sInstance;
+    }
+
+    synchronized boolean connectInputDevice(BluetoothDevice device,
+                                            BluetoothDeviceProfileState state) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        if (objectPath == null ||
+            getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
+            getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
+            return false;
+        }
+        if (state != null) {
+            Message msg = new Message();
+            msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
+            msg.obj = state;
+            mHidProfileState.sendMessage(msg);
+            return true;
+        }
+        return false;
+    }
+
+    synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
+        if (!mBluetoothService.connectInputDeviceNative(objectPath)) {
+            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
+            return false;
+        }
+        return true;
+    }
+
+    synchronized boolean disconnectInputDevice(BluetoothDevice device,
+                                               BluetoothDeviceProfileState state) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        if (objectPath == null ||
+                getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) {
+            return false;
+        }
+        if (state != null) {
+            Message msg = new Message();
+            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
+            msg.obj = state;
+            mHidProfileState.sendMessage(msg);
+            return true;
+        }
+        return false;
+    }
+
+    synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
+        if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) {
+            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
+            return false;
+        }
+        return true;
+    }
+
+    synchronized int getInputDeviceState(BluetoothDevice device) {
+        if (mInputDevices.get(device) == null) {
+            return BluetoothInputDevice.STATE_DISCONNECTED;
+        }
+        return mInputDevices.get(device);
+    }
+
+    synchronized List<BluetoothDevice> getConnectedInputDevices() {
+        List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
+            new int[] {BluetoothInputDevice.STATE_CONNECTED});
+        return devices;
+    }
+
+    synchronized int getInputDevicePriority(BluetoothDevice device) {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+                BluetoothInputDevice.PRIORITY_UNDEFINED);
+    }
+
+    synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+            return false;
+        }
+        return Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+                priority);
+    }
+
+    synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
+
+        for (BluetoothDevice device: mInputDevices.keySet()) {
+            int inputDeviceState = getInputDeviceState(device);
+            for (int state : states) {
+                if (state == inputDeviceState) {
+                    inputDevices.add(device);
+                    break;
+                }
+            }
+        }
+        return inputDevices;
+    }
+
+    private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
+        int prevState;
+        if (mInputDevices.get(device) == null) {
+            prevState = BluetoothInputDevice.STATE_DISCONNECTED;
+        } else {
+            prevState = mInputDevices.get(device);
+        }
+        if (prevState == state) return;
+
+        mInputDevices.put(device, state);
+
+        if (getInputDevicePriority(device) >
+              BluetoothInputDevice.PRIORITY_OFF &&
+            state == BluetoothInputDevice.STATE_CONNECTING ||
+            state == BluetoothInputDevice.STATE_CONNECTED) {
+            // We have connected or attempting to connect.
+            // Bump priority
+            setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
+        }
+
+        Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
+        intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
+        mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
+
+        debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
+        mBluetoothService.sendConnectionStateChange(device, state, prevState);
+    }
+
+    synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
+        int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
+            BluetoothInputDevice.STATE_DISCONNECTED;
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothDevice device = adapter.getRemoteDevice(address);
+        handleInputDeviceStateChange(device, state);
+    }
+
+    synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) {
+        switch (state) {
+            case BluetoothDevice.BOND_BONDED:
+                if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
+                    setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
+                }
+                break;
+            case BluetoothDevice.BOND_NONE:
+                setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
+                break;
+        }
+    }
+
+    private static void debugLog(String msg) {
+        if (DBG) Log.d(TAG, msg);
+    }
+
+    private static void errorLog(String msg) {
+        Log.e(TAG, msg);
+    }
+}
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java
new file mode 100644 (file)
index 0000000..fb96439
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2011 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 android.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources.NotFoundException;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This handles the PAN profile. All calls into this are made
+ * from Bluetooth Service.
+ */
+final class BluetoothPanProfileHandler {
+    private static final String TAG = "BluetoothPanProfileHandler";
+    private static final boolean DBG = true;
+
+    private ArrayList<String> mBluetoothIfaceAddresses;
+    private int mMaxPanDevices;
+
+    private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
+    private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
+    private static final int BLUETOOTH_PREFIX_LENGTH        = 24;
+    public static BluetoothPanProfileHandler sInstance;
+    private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
+    private boolean mTetheringOn;
+    private Context mContext;
+    private BluetoothService mBluetoothService;
+
+    private BluetoothPanProfileHandler(Context context, BluetoothService service) {
+        mContext = context;
+        mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
+        mBluetoothService = service;
+        mTetheringOn = false;
+        mBluetoothIfaceAddresses = new ArrayList<String>();
+        try {
+            mMaxPanDevices = context.getResources().getInteger(
+                            com.android.internal.R.integer.config_max_pan_devices);
+        } catch (NotFoundException e) {
+            mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
+        }
+    }
+
+    static synchronized BluetoothPanProfileHandler getInstance(Context context,
+            BluetoothService service) {
+        if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service);
+        return sInstance;
+    }
+
+    synchronized boolean isTetheringOn() {
+        return mTetheringOn;
+    }
+
+    synchronized boolean allowIncomingTethering() {
+        if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
+            return true;
+        return false;
+    }
+
+    private BroadcastReceiver mTetheringReceiver = null;
+
+    synchronized void setBluetoothTethering(boolean value) {
+        if (!value) {
+            disconnectPanServerDevices();
+        }
+
+        if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+            mTetheringReceiver = new BroadcastReceiver() {
+                @Override
+                public synchronized void onReceive(Context context, Intent intent) {
+                    if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
+                            == BluetoothAdapter.STATE_ON) {
+                        mTetheringOn = true;
+                        mContext.unregisterReceiver(mTetheringReceiver);
+                    }
+                }
+            };
+            mContext.registerReceiver(mTetheringReceiver, filter);
+        } else {
+            mTetheringOn = value;
+        }
+    }
+
+    synchronized int getPanDeviceState(BluetoothDevice device) {
+        BluetoothPanDevice panDevice = mPanDevices.get(device);
+        if (panDevice == null) {
+            return BluetoothPan.STATE_DISCONNECTED;
+        }
+        return panDevice.mState;
+    }
+
+    synchronized boolean connectPanDevice(BluetoothDevice device) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")");
+        if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
+            errorLog(device + " already connected to PAN");
+        }
+
+        int connectedCount = 0;
+        for (BluetoothDevice panDevice: mPanDevices.keySet()) {
+            if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
+                connectedCount ++;
+            }
+        }
+        if (connectedCount > 8) {
+            debugLog(device + " could not connect to PAN because 8 other devices are"
+                    + "already connected");
+            return false;
+        }
+
+        handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING,
+                                           BluetoothPan.LOCAL_PANU_ROLE);
+        if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) {
+            debugLog("connecting to PAN");
+            return true;
+        } else {
+            handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED,
+                                                BluetoothPan.LOCAL_PANU_ROLE);
+            errorLog("could not connect to PAN");
+            return false;
+        }
+    }
+
+    private synchronized boolean disconnectPanServerDevices() {
+        debugLog("disconnect all PAN devices");
+
+        for (BluetoothDevice device: mPanDevices.keySet()) {
+            BluetoothPanDevice panDevice = mPanDevices.get(device);
+            int state = panDevice.mState;
+            if (state == BluetoothPan.STATE_CONNECTED &&
+                    panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
+                String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+
+                handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
+                        panDevice.mLocalRole);
+
+                if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath,
+                        device.getAddress(),
+                        panDevice.mIfaceAddr)) {
+                    errorLog("could not disconnect Pan Server Device "+device.getAddress());
+
+                    // Restore prev state
+                    handlePanDeviceStateChange(device, state,
+                            panDevice.mLocalRole);
+
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    synchronized List<BluetoothDevice> getConnectedPanDevices() {
+        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+        for (BluetoothDevice device: mPanDevices.keySet()) {
+            if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+                devices.add(device);
+            }
+        }
+        return devices;
+    }
+
+    synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+        debugLog("disconnect PAN(" + objectPath + ")");
+
+        int state = getPanDeviceState(device);
+        if (state != BluetoothPan.STATE_CONNECTED) {
+            debugLog(device + " already disconnected from PAN");
+            return false;
+        }
+
+        BluetoothPanDevice panDevice = mPanDevices.get(device);
+
+        if (panDevice == null) {
+            errorLog("No record for this Pan device:" + device);
+            return false;
+        }
+
+        handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
+                                    panDevice.mLocalRole);
+        if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
+            if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(),
+                    panDevice.mIface)) {
+                // Restore prev state, this shouldn't happen
+                handlePanDeviceStateChange(device, state, panDevice.mLocalRole);
+                return false;
+            }
+        } else {
+            if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) {
+                // Restore prev state, this shouldn't happen
+                handlePanDeviceStateChange(device, state, panDevice.mLocalRole);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                 String iface, int state, int role) {
+        int prevState;
+        String ifaceAddr = null;
+        BluetoothPanDevice panDevice = mPanDevices.get(device);
+
+        if (panDevice == null) {
+            prevState = BluetoothPan.STATE_DISCONNECTED;
+        } else {
+            prevState = panDevice.mState;
+            ifaceAddr = panDevice.mIfaceAddr;
+        }
+        if (prevState == state) return;
+
+        if (role == BluetoothPan.LOCAL_NAP_ROLE) {
+            if (state == BluetoothPan.STATE_CONNECTED) {
+                ifaceAddr = enableTethering(iface);
+                if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
+            } else if (state == BluetoothPan.STATE_DISCONNECTED) {
+                if (ifaceAddr != null) {
+                    mBluetoothIfaceAddresses.remove(ifaceAddr);
+                    ifaceAddr = null;
+                }
+            }
+        } else {
+            // PANU Role = reverse Tether
+            if (state == BluetoothPan.STATE_CONNECTED) {
+                BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
+            } else if (state == BluetoothPan.STATE_DISCONNECTED &&
+                  (prevState == BluetoothPan.STATE_CONNECTED ||
+                  prevState == BluetoothPan.STATE_DISCONNECTING)) {
+                BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface);
+            }
+        }
+
+        if (panDevice == null) {
+            panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role);
+            mPanDevices.put(device, panDevice);
+        } else {
+            panDevice.mState = state;
+            panDevice.mIfaceAddr = ifaceAddr;
+            panDevice.mLocalRole = role;
+        }
+
+        if (state == BluetoothPan.STATE_DISCONNECTED) {
+            mPanDevices.remove(device);
+        }
+
+        Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
+        intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
+        intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
+        mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
+
+        debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
+        mBluetoothService.sendConnectionStateChange(device, state, prevState);
+    }
+
+    synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                 int state, int role) {
+        handlePanDeviceStateChange(device, null, state, role);
+    }
+
+    private class BluetoothPanDevice {
+        private int mState;
+        private String mIfaceAddr;
+        private String mIface;
+        private int mLocalRole; // Which local role is this PAN device bound to
+
+        BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
+            mState = state;
+            mIfaceAddr = ifaceAddr;
+            mIface = iface;
+            mLocalRole = localRole;
+        }
+    }
+
+    private String createNewTetheringAddressLocked() {
+        if (getConnectedPanDevices().size() == mMaxPanDevices) {
+            debugLog ("Max PAN device connections reached");
+            return null;
+        }
+        String address = BLUETOOTH_IFACE_ADDR_START;
+        while (true) {
+            if (mBluetoothIfaceAddresses.contains(address)) {
+                String[] addr = address.split("\\.");
+                Integer newIp = Integer.parseInt(addr[2]) + 1;
+                address = address.replace(addr[2], newIp.toString());
+            } else {
+                break;
+            }
+        }
+        mBluetoothIfaceAddresses.add(address);
+        return address;
+    }
+
+    // configured when we start tethering
+    private synchronized String enableTethering(String iface) {
+        debugLog("updateTetherState:" + iface);
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        ConnectivityManager cm =
+            (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+        // bring toggle the interfaces
+        String[] currentIfaces = new String[0];
+        try {
+            currentIfaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return null;
+        }
+
+        boolean found = false;
+        for (String currIface: currentIfaces) {
+            if (currIface.equals(iface)) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) return null;
+
+        String address = createNewTetheringAddressLocked();
+        if (address == null) return null;
+
+        InterfaceConfiguration ifcg = null;
+        try {
+            ifcg = service.getInterfaceConfig(iface);
+            if (ifcg != null) {
+                InetAddress addr = null;
+                if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
+                        addr.equals(InetAddress.getByName("0.0.0.0")) ||
+                        addr.equals(InetAddress.getByName("::0"))) {
+                    addr = InetAddress.getByName(address);
+                }
+                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+                ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
+                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
+                service.setInterfaceConfig(iface, ifcg);
+                if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                    Log.e(TAG, "Error tethering "+iface);
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+            return null;
+        }
+        return address;
+    }
+
+    private static void debugLog(String msg) {
+        if (DBG) Log.d(TAG, msg);
+    }
+
+    private static void errorLog(String msg) {
+        Log.e(TAG, msg);
+    }
+}
index b6101b2..a295de5 100644 (file)
 
 package android.server;
 
+import com.android.internal.app.IBatteryStats;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothDeviceProfileState;
 import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfileState;
 import android.bluetooth.BluetoothSocket;
-import android.bluetooth.BluetoothTetheringDataTracker;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothCallback;
@@ -44,14 +44,9 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.res.Resources.NotFoundException;
-import android.net.ConnectivityManager;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -60,8 +55,6 @@ import android.provider.Settings;
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.internal.app.IBatteryStats;
-
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -76,7 +69,6 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -96,7 +88,6 @@ public class BluetoothService extends IBluetooth.Stub {
     private int mBluetoothState;
     private boolean mRestart = false;  // need to call enable() after disable()
     private boolean mIsDiscovering;
-    private boolean mTetheringOn;
     private int[] mAdapterSdpHandles;
     private ParcelUuid[] mAdapterUuids;
 
@@ -106,7 +97,7 @@ public class BluetoothService extends IBluetooth.Stub {
     private final Context mContext;
 
     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
-    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
     private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
     private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
@@ -125,13 +116,6 @@ public class BluetoothService extends IBluetooth.Stub {
     private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
     private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
 
-    private ArrayList<String> mBluetoothIfaceAddresses;
-    private int mMaxPanDevices;
-
-    private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
-    private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
-    private static final int BLUETOOTH_PREFIX_LENGTH    = 24;
-
     // The timeout used to sent the UUIDs Intent
     // This timeout should be greater than the page timeout
     private static final int UUID_INTENT_DELAY = 6000;
@@ -155,11 +139,8 @@ public class BluetoothService extends IBluetooth.Stub {
     private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
     private final BluetoothProfileState mA2dpProfileState;
     private final BluetoothProfileState mHfpProfileState;
-    private final BluetoothProfileState mHidProfileState;
 
     private BluetoothA2dpService mA2dpService;
-    private final HashMap<BluetoothDevice, Integer> mInputDevices;
-    private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
     private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
 
     private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
@@ -167,9 +148,9 @@ public class BluetoothService extends IBluetooth.Stub {
     private static String mDockAddress;
     private String mDockPin;
 
-    private String mIface;
-
     private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+    private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
+    private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
 
     private static class RemoteService {
         public String address;
@@ -218,7 +199,7 @@ public class BluetoothService extends IBluetooth.Stub {
 
         mBluetoothState = BluetoothAdapter.STATE_OFF;
         mIsDiscovering = false;
-        mTetheringOn = false;
+
         mAdapterProperties = new HashMap<String, String>();
         mDeviceProperties = new HashMap<String, Map<String,String>>();
 
@@ -230,27 +211,17 @@ public class BluetoothService extends IBluetooth.Stub {
         mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
         mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
         mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
-        mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
-
-        mBluetoothIfaceAddresses = new ArrayList<String>();
-        try {
-            mMaxPanDevices = context.getResources().getInteger(
-                            com.android.internal.R.integer.config_max_pan_devices);
-        } catch (NotFoundException e) {
-            mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
-        }
 
         mHfpProfileState.start();
         mA2dpProfileState.start();
-        mHidProfileState.start();
 
         IntentFilter filter = new IntentFilter();
         registerForAirplaneMode(filter);
 
         filter.addAction(Intent.ACTION_DOCK_EVENT);
         mContext.registerReceiver(mReceiver, filter);
-        mInputDevices = new HashMap<BluetoothDevice, Integer>();
-        mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>();
+        mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
+        mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
     }
 
     public static synchronized String readDockBluetoothAddress() {
@@ -590,21 +561,16 @@ public class BluetoothService extends IBluetooth.Stub {
                     persistBluetoothOnSetting(true);
                 }
 
-                updateSdpRecords();
-
                 mIsDiscovering = false;
                 mBondState.readAutoPairingData();
                 mBondState.loadBondState();
                 initProfileState();
 
-                // Log bluetooth on to battery stats.
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    mBatteryStats.noteBluetoothOn();
-                } catch (RemoteException e) {
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                // This should be the last step of the the enable thread.
+                // Because this adds SDP records which asynchronously
+                // broadcasts the Bluetooth On State in updateBluetoothState.
+                // So we want all internal state setup before this.
+                updateSdpRecords();
             } else {
                 setBluetoothState(BluetoothAdapter.STATE_OFF);
             }
@@ -652,6 +618,11 @@ public class BluetoothService extends IBluetooth.Stub {
         }
     }
 
+    /**
+     * This function is called from Bluetooth Event Loop when onPropertyChanged
+     * for adapter comes in with UUID property.
+     * @param uuidsThe uuids of adapter as reported by Bluez.
+     */
     synchronized void updateBluetoothState(String uuids) {
         if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) {
             ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
@@ -662,6 +633,15 @@ public class BluetoothService extends IBluetooth.Stub {
                 String[] propVal = {"Pairable", getProperty("Pairable")};
                 mEventLoop.onPropertyChanged(propVal);
 
+                // Log bluetooth on to battery stats.
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mBatteryStats.noteBluetoothOn();
+                } catch (RemoteException e) {
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+
                 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
                     disable(false);
                 }
@@ -825,7 +805,8 @@ public class BluetoothService extends IBluetooth.Stub {
 
             // HID is handled by BluetoothService, other profiles
             // will be handled by their respective services.
-            setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
+            mBluetoothInputProfileHandler.setInitialInputDevicePriority(
+                    mAdapter.getRemoteDevice(address), state);
 
             if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
                          reason + ")");
@@ -1472,424 +1453,6 @@ public class BluetoothService extends IBluetooth.Stub {
         return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
     }
 
-    public synchronized boolean isTetheringOn() {
-        return mTetheringOn;
-    }
-
-    /*package*/ synchronized boolean allowIncomingTethering() {
-        if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
-            return true;
-        return false;
-    }
-
-    private BroadcastReceiver mTetheringReceiver = null;
-
-    public synchronized void setBluetoothTethering(boolean value) {
-        if (!value) {
-            disconnectPan();
-        }
-
-        if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-            mTetheringReceiver = new BroadcastReceiver() {
-                @Override
-                public synchronized void onReceive(Context context, Intent intent) {
-                    if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
-                            == BluetoothAdapter.STATE_ON) {
-                        mTetheringOn = true;
-                        mContext.unregisterReceiver(mTetheringReceiver);
-                    }
-                }
-            };
-            mContext.registerReceiver(mTetheringReceiver, filter);
-        } else {
-            mTetheringOn = value;
-        }
-    }
-
-    public synchronized int getPanDeviceState(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
-        Pair<Integer, String> panDevice = mPanDevices.get(device);
-        if (panDevice == null) {
-            return BluetoothPan.STATE_DISCONNECTED;
-        }
-        return panDevice.first;
-    }
-
-    public synchronized boolean connectPanDevice(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        if (DBG) log("connect PAN(" + objectPath + ")");
-        if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
-            log (device + " already connected to PAN");
-        }
-
-        int connectedCount = 0;
-        for (BluetoothDevice panDevice: mPanDevices.keySet()) {
-            if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
-                connectedCount ++;
-            }
-        }
-        if (connectedCount > 8) {
-            log (device + " could not connect to PAN because 8 other devices are already connected");
-            return false;
-        }
-
-        handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING,
-                                           BluetoothPan.LOCAL_PANU_ROLE);
-        if (connectPanDeviceNative(objectPath, "nap")) {
-            log ("connecting to PAN");
-            return true;
-        } else {
-            handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED,
-                                                BluetoothPan.LOCAL_PANU_ROLE);
-            log ("could not connect to PAN");
-            return false;
-        }
-    }
-
-    private synchronized boolean disconnectPan() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (DBG) log("disconnect all PAN devices");
-
-        for (BluetoothDevice device: mPanDevices.keySet()) {
-            if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
-                if (!disconnectPanDevice(device)) {
-                    log ("could not disconnect Pan Device "+device.getAddress());
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    public synchronized List<BluetoothDevice> getConnectedPanDevices() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-
-        for (BluetoothDevice device: mPanDevices.keySet()) {
-            if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
-                devices.add(device);
-            }
-        }
-        return devices;
-    }
-
-    public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        if (DBG) log("disconnect PAN(" + objectPath + ")");
-        if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
-            log (device + " already disconnected from PAN");
-        }
-        handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
-                                    BluetoothPan.LOCAL_PANU_ROLE);
-        return disconnectPanDeviceNative(objectPath);
-    }
-
-    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
-                                                             String iface,
-                                                             int state,
-                                                             int role) {
-        int prevState;
-        String ifaceAddr = null;
-
-        if (mPanDevices.get(device) == null) {
-            prevState = BluetoothPan.STATE_DISCONNECTED;
-        } else {
-            prevState = mPanDevices.get(device).first;
-            ifaceAddr = mPanDevices.get(device).second;
-        }
-        if (prevState == state) return;
-
-        if (role == BluetoothPan.LOCAL_NAP_ROLE) {
-            if (state == BluetoothPan.STATE_CONNECTED) {
-                ifaceAddr = enableTethering(iface);
-                if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
-            } else if (state == BluetoothPan.STATE_DISCONNECTED) {
-                if (ifaceAddr != null) {
-                    mBluetoothIfaceAddresses.remove(ifaceAddr);
-                    ifaceAddr = null;
-                }
-            }
-        } else {
-            // PANU Role = reverse Tether
-            if (state == BluetoothPan.STATE_CONNECTED) {
-                mIface = iface;
-                BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
-            } else if (state == BluetoothPan.STATE_DISCONNECTED &&
-                  (prevState == BluetoothPan.STATE_CONNECTED ||
-                  prevState == BluetoothPan.STATE_DISCONNECTING)) {
-                BluetoothTetheringDataTracker.getInstance().stopReverseTether(mIface);
-            }
-        }
-
-        Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr);
-        mPanDevices.put(device, value);
-
-        Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
-        intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
-        intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
-        if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
-        sendConnectionStateChange(device, state, prevState);
-    }
-
-    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
-                                                             int state, int role) {
-        handlePanDeviceStateChange(device, null, state, role);
-    }
-
-    private String createNewTetheringAddressLocked() {
-        if (getConnectedPanDevices().size() == mMaxPanDevices) {
-            log("Max PAN device connections reached");
-            return null;
-        }
-        String address = BLUETOOTH_IFACE_ADDR_START;
-        while (true) {
-            if (mBluetoothIfaceAddresses.contains(address)) {
-                String[] addr = address.split("\\.");
-                Integer newIp = Integer.parseInt(addr[2]) + 1;
-                address = address.replace(addr[2], newIp.toString());
-            } else {
-                break;
-            }
-        }
-        mBluetoothIfaceAddresses.add(address);
-        return address;
-    }
-
-    // configured when we start tethering
-    private synchronized String enableTethering(String iface) {
-        log("updateTetherState:" + iface);
-
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-        ConnectivityManager cm =
-            (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
-
-        // bring toggle the interfaces
-        String[] currentIfaces = new String[0];
-        try {
-            currentIfaces = service.listInterfaces();
-        } catch (Exception e) {
-            Log.e(TAG, "Error listing Interfaces :" + e);
-            return null;
-        }
-
-        boolean found = false;
-        for (String currIface: currentIfaces) {
-            if (currIface.equals(iface)) {
-                found = true;
-                break;
-            }
-        }
-
-        if (!found) return null;
-
-        String address = createNewTetheringAddressLocked();
-        if (address == null) return null;
-
-        InterfaceConfiguration ifcg = null;
-        try {
-            ifcg = service.getInterfaceConfig(iface);
-            if (ifcg != null) {
-                InetAddress addr = null;
-                if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
-                        addr.equals(InetAddress.getByName("0.0.0.0")) ||
-                        addr.equals(InetAddress.getByName("::0"))) {
-                    addr = InetAddress.getByName(address);
-                }
-                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
-                ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
-                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
-                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
-                service.setInterfaceConfig(iface, ifcg);
-                if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
-                    Log.e(TAG, "Error tethering "+iface);
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
-            return null;
-        }
-        return address;
-    }
-
-    public synchronized boolean connectInputDevice(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        if (objectPath == null ||
-            getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
-            getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
-            return false;
-        }
-        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
-        if (state != null) {
-            Message msg = new Message();
-            msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
-            msg.obj = state;
-            mHidProfileState.sendMessage(msg);
-            return true;
-        }
-        return false;
-    }
-
-    public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
-        if (!connectInputDeviceNative(objectPath)) {
-            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
-            return false;
-        }
-        return true;
-    }
-
-    public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        if (objectPath == null ||
-                getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) {
-            return false;
-        }
-        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
-        if (state != null) {
-            Message msg = new Message();
-            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
-            msg.obj = state;
-            mHidProfileState.sendMessage(msg);
-            return true;
-        }
-        return false;
-    }
-
-    public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
-        String objectPath = getObjectPathFromAddress(device.getAddress());
-        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
-        if (!disconnectInputDeviceNative(objectPath)) {
-            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
-            return false;
-        }
-        return true;
-    }
-
-    public synchronized int getInputDeviceState(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
-        if (mInputDevices.get(device) == null) {
-            return BluetoothInputDevice.STATE_DISCONNECTED;
-        }
-        return mInputDevices.get(device);
-    }
-
-    public synchronized List<BluetoothDevice> getConnectedInputDevices() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
-            new int[] {BluetoothInputDevice.STATE_CONNECTED});
-        return devices;
-    }
-
-    public synchronized int getInputDevicePriority(BluetoothDevice device) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
-                BluetoothInputDevice.PRIORITY_UNDEFINED);
-    }
-
-    public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
-            return false;
-        }
-        return Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
-                priority);
-    }
-
-    /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
-        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
-
-        for (BluetoothDevice device: mInputDevices.keySet()) {
-            int inputDeviceState = getInputDeviceState(device);
-            for (int state : states) {
-                if (state == inputDeviceState) {
-                    inputDevices.add(device);
-                    break;
-                }
-            }
-        }
-        return inputDevices;
-    }
-
-    private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
-        int prevState;
-        if (mInputDevices.get(device) == null) {
-            prevState = BluetoothInputDevice.STATE_DISCONNECTED;
-        } else {
-            prevState = mInputDevices.get(device);
-        }
-        if (prevState == state) return;
-
-        mInputDevices.put(device, state);
-
-        if (getInputDevicePriority(device) >
-              BluetoothInputDevice.PRIORITY_OFF &&
-            state == BluetoothInputDevice.STATE_CONNECTING ||
-            state == BluetoothInputDevice.STATE_CONNECTED) {
-            // We have connected or attempting to connect.
-            // Bump priority
-            setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
-        }
-
-        Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
-        intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
-        if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
-        sendConnectionStateChange(device, state, prevState);
-    }
-
-    /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
-        int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
-            BluetoothInputDevice.STATE_DISCONNECTED;
-        BluetoothDevice device = mAdapter.getRemoteDevice(address);
-        handleInputDeviceStateChange(device, state);
-    }
-
-    private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
-        switch (state) {
-            case BluetoothDevice.BOND_BONDED:
-                if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
-                    setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
-                }
-                break;
-            case BluetoothDevice.BOND_NONE:
-                setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
-                break;
-        }
-    }
-
-    /*package*/ boolean isRemoteDeviceInCache(String address) {
-        return (mDeviceProperties.get(address) != null);
-    }
-
     /*package*/ String[] getRemoteDeviceProperties(String address) {
         if (!isEnabledInternal()) return null;
 
@@ -2674,6 +2237,114 @@ public class BluetoothService extends IBluetooth.Stub {
         if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
     }
 
+    /**** Handlers for PAN  Profile ****/
+
+    public synchronized boolean isTetheringOn() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothPanProfileHandler.isTetheringOn();
+    }
+
+    /*package*/ synchronized boolean allowIncomingTethering() {
+        return mBluetoothPanProfileHandler.allowIncomingTethering();
+    }
+
+    public synchronized void setBluetoothTethering(boolean value) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        mBluetoothPanProfileHandler.setBluetoothTethering(value);
+    }
+
+    public synchronized int getPanDeviceState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothPanProfileHandler.getPanDeviceState(device);
+    }
+
+    public synchronized boolean connectPanDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+            "Need BLUETOOTH_ADMIN permission");
+        return mBluetoothPanProfileHandler.connectPanDevice(device);
+    }
+
+    public synchronized List<BluetoothDevice> getConnectedPanDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothPanProfileHandler.getConnectedPanDevices();
+    }
+
+    public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+            "Need BLUETOOTH_ADMIN permission");
+        return mBluetoothPanProfileHandler.disconnectPanDevice(device);
+    }
+
+    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                             String iface,
+                                                             int state,
+                                                             int role) {
+        mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
+    }
+
+    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                             int state, int role) {
+        mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
+    }
+
+    /**** Handlers for Input Device Profile ****/
+
+    public synchronized boolean connectInputDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+        return mBluetoothInputProfileHandler.connectInputDevice(device, state);
+    }
+
+    public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+        return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
+    }
+
+    public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+        return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
+    }
+
+    public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+        return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
+    }
+
+    public synchronized int getInputDeviceState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothInputProfileHandler.getInputDeviceState(device);
+
+    }
+
+    public synchronized List<BluetoothDevice> getConnectedInputDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothInputProfileHandler.getConnectedInputDevices();
+    }
+
+    public synchronized int getInputDevicePriority(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mBluetoothInputProfileHandler.getInputDevicePriority(device);
+    }
+
+    public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
+    }
+
+    /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+        return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
+    }
+
+    /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
+        mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
+    }
+
+    /*package*/ boolean isRemoteDeviceInCache(String address) {
+        return (mDeviceProperties.get(address) != null);
+    }
+
     public boolean connectHeadset(String address) {
         if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
 
@@ -2927,12 +2598,14 @@ public class BluetoothService extends IBluetooth.Stub {
             short channel);
     private native boolean removeServiceRecordNative(int handle);
     private native boolean setLinkTimeoutNative(String path, int num_slots);
-    private native boolean connectInputDeviceNative(String path);
-    private native boolean disconnectInputDeviceNative(String path);
-
-    private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
-    private native boolean connectPanDeviceNative(String path, String dstRole);
-    private native boolean disconnectPanDeviceNative(String path);
+    native boolean connectInputDeviceNative(String path);
+    native boolean disconnectInputDeviceNative(String path);
+
+    native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
+    native boolean connectPanDeviceNative(String path, String dstRole);
+    native boolean disconnectPanDeviceNative(String path);
+    native boolean disconnectPanServerDeviceNative(String path,
+            String address, String iface);
 
     private native int[] addReservedServiceRecordsNative(int[] uuuids);
     private native boolean removeReservedServiceRecordsNative(int[] handles);
index 8700af8..97a216a 100644 (file)
@@ -600,8 +600,9 @@ public abstract class Layout {
      * are at different run levels (and thus there's a split caret).
      * @param offset the offset
      * @return true if at a level boundary
+     * @hide
      */
-    private boolean isLevelBoundary(int offset) {
+    public boolean isLevelBoundary(int offset) {
         int line = getLineForOffset(offset);
         Directions dirs = getLineDirections(line);
         if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
@@ -1148,8 +1149,7 @@ public abstract class Layout {
         int bottom = getLineTop(line+1);
 
         float h1 = getPrimaryHorizontal(point) - 0.5f;
-        float h2 = isLevelBoundary(point) ?
-                    getSecondaryHorizontal(point) - 0.5f : h1;
+        float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1;
 
         int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
                    TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
index 03a6aa5..b982c7b 100644 (file)
@@ -1421,55 +1421,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
     }
 
     /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is showing the last item.
-     * @hide
-     */
-    protected static final int[] LAST_STATE_SET = {R.attr.state_last};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is showing the first item.
-     * @hide
-     */
-    protected static final int[] FIRST_STATE_SET = {R.attr.state_first};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is showing the middle item.
-     * @hide
-     */
-    protected static final int[] MIDDLE_STATE_SET = {R.attr.state_middle};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is showing only one item.
-     * @hide
-     */
-    protected static final int[] SINGLE_STATE_SET = {R.attr.state_single};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is pressed and showing the last item.
-     * @hide
-     */
-    protected static final int[] PRESSED_LAST_STATE_SET = {R.attr.state_last, R.attr.state_pressed};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is pressed and showing the first item.
-     * @hide
-     */
-    protected static final int[] PRESSED_FIRST_STATE_SET = {R.attr.state_first, R.attr.state_pressed};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is pressed and showing the middle item.
-     * @hide
-     */
-    protected static final int[] PRESSED_MIDDLE_STATE_SET = {R.attr.state_middle, R.attr.state_pressed};
-    /**
-     * Used by views that contain lists of items. This state indicates that
-     * the view is pressed and showing only one item.
-     * @hide
-     */
-    protected static final int[] PRESSED_SINGLE_STATE_SET = {R.attr.state_single, R.attr.state_pressed};
-
-    /**
      * Temporary Rect currently for use in setBackground().  This will probably
      * be extended in the future to hold our own class with more than just
      * a Rect. :)
@@ -1497,14 +1448,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    /*package*/ int mMeasuredWidth;
+    int mMeasuredWidth;
 
     /**
      * Height as measured during measure pass.
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    /*package*/ int mMeasuredHeight;
+    int mMeasuredHeight;
 
     /**
      * Flag to indicate that this view was marked INVALIDATED, or had its display list
@@ -2187,6 +2138,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
 
     private int[] mDrawableState = null;
 
+    /**
+     * Set to true when drawing cache is enabled and cannot be created.
+     * 
+     * @hide
+     */
+    public boolean mCachingFailed;
+
     private Bitmap mDrawingCache;
     private Bitmap mUnscaledDrawingCache;
     private DisplayList mDisplayList;
@@ -2283,6 +2241,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
      * with this flag set, all visible applications will be able to participate
      * in the drag operation and receive the dragged content.
+     *
+     * @hide
      */
     public static final int DRAG_FLAG_GLOBAL = 1;
 
@@ -3749,16 +3709,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
     }
 
     /**
-     * Determine if this view has the FITS_SYSTEM_WINDOWS flag set.
-     * @return True if window has FITS_SYSTEM_WINDOWS set
-     *
-     * @hide
-     */
-    public boolean isFitsSystemWindowsFlagSet() {
-        return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
-    }
-
-    /**
      * Returns the visibility status for this view.
      *
      * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -8414,6 +8364,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      * @see #setLayerType(int, android.graphics.Paint)
      */
     public void setDrawingCacheEnabled(boolean enabled) {
+        mCachingFailed = false;
         setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
     }
 
@@ -8436,6 +8387,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      *
      * @hide
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     public void outputDirtyFlags(String indent, boolean clear, int clearMask) {
         Log.d("View", indent + this + "             DIRTY(" + (mPrivateFlags & View.DIRTY_MASK) +
                 ") DRAWN(" + (mPrivateFlags & DRAWN) + ")" + " CACHE_VALID(" +
@@ -8473,10 +8425,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      * @hide
      */
     public boolean canHaveDisplayList() {
-        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
-            return false;
-        }
-        return true;
+        return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
     }
 
     /**
@@ -8658,7 +8607,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
     public void buildDrawingCache() {
         buildDrawingCache(false);
     }
-
+    
     /**
      * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
      *
@@ -8685,6 +8634,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
+            mCachingFailed = false;
 
             if (ViewDebug.TRACE_HIERARCHY) {
                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -8710,6 +8660,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
                     (width * height * (opaque && !use32BitCache ? 2 : 4) >
                             ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
                 destroyDrawingCache();
+                mCachingFailed = true;
                 return;
             }
 
@@ -8719,12 +8670,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
             if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
                 Bitmap.Config quality;
                 if (!opaque) {
+                    // Never pick ARGB_4444 because it looks awful
+                    // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
                     switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
                         case DRAWING_CACHE_QUALITY_AUTO:
                             quality = Bitmap.Config.ARGB_8888;
                             break;
                         case DRAWING_CACHE_QUALITY_LOW:
-                            quality = Bitmap.Config.ARGB_4444;
+                            quality = Bitmap.Config.ARGB_8888;
                             break;
                         case DRAWING_CACHE_QUALITY_HIGH:
                             quality = Bitmap.Config.ARGB_8888;
@@ -8760,6 +8713,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
                     } else {
                         mUnscaledDrawingCache = null;
                     }
+                    mCachingFailed = true;
                     return;
                 }
 
@@ -11324,6 +11278,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
      * </p>
      */
     public boolean dispatchDragEvent(DragEvent event) {
+        //noinspection SimplifiableIfStatement
         if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnDragListener.onDrag(this, event)) {
             return true;
index 0444496..cc4e89c 100644 (file)
@@ -152,12 +152,12 @@ public class ViewConfiguration {
      * should be at least equal to the size of the screen in ARGB888 format.
      */
     @Deprecated
-    private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
+    private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888
 
     /**
      * The coefficient of friction applied to flings/scrolls.
      */
-    private static float SCROLL_FRICTION = 0.015f;
+    private static final float SCROLL_FRICTION = 0.015f;
 
     /**
      * Max distance to overscroll for edge effects
index ad6b0f6..b5a2558 100644 (file)
 package android.view;
 
 import android.animation.LayoutTransition;
-import android.view.animation.AlphaAnimation;
-import com.android.internal.R;
-import com.android.internal.util.Predicate;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -39,10 +35,13 @@ import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LayoutAnimationController;
 import android.view.animation.Transformation;
+import com.android.internal.R;
+import com.android.internal.util.Predicate;
 
 import java.util.ArrayList;
 import java.util.HashSet;
index 0a9386d..25f2229 100644 (file)
@@ -19,9 +19,11 @@ package android.view.inputmethod;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -32,12 +34,17 @@ import java.util.List;
  * specified subtype of the designated input method directly.
  */
 public final class InputMethodSubtype implements Parcelable {
+    private static final String TAG = InputMethodSubtype.class.getSimpleName();
+    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
+    private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
+
     private final int mSubtypeNameResId;
     private final int mSubtypeIconResId;
     private final String mSubtypeLocale;
     private final String mSubtypeMode;
     private final String mSubtypeExtraValue;
     private final int mSubtypeHashCode;
+    private HashMap<String, String> mExtraValueHashMapCache;
 
     /**
      * Constructor
@@ -106,6 +113,46 @@ public final class InputMethodSubtype implements Parcelable {
         return mSubtypeExtraValue;
     }
 
+    private HashMap<String, String> getExtraValueHashMap() {
+        if (mExtraValueHashMapCache == null) {
+            mExtraValueHashMapCache = new HashMap<String, String>();
+            final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
+            final int N = pairs.length;
+            for (int i = 0; i < N; ++i) {
+                final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
+                if (pair.length == 1) {
+                    mExtraValueHashMapCache.put(pair[0], null);
+                } else if (pair.length > 1) {
+                    if (pair.length > 2) {
+                        Slog.w(TAG, "ExtraValue has two or more '='s");
+                    }
+                    mExtraValueHashMapCache.put(pair[0], pair[1]);
+                }
+            }
+        }
+        return mExtraValueHashMapCache;
+    }
+
+    /**
+     * The string of ExtraValue in subtype should be defined as follows:
+     * example: key0,key1=value1,key2,key3,key4=value4
+     * @param key the key of extra value
+     * @return the subtype contains specified the extra value
+     */
+    public boolean containsExtraValueKey(String key) {
+        return getExtraValueHashMap().containsKey(key);
+    }
+
+    /**
+     * The string of ExtraValue in subtype should be defined as follows:
+     * example: key0,key1=value1,key2,key3,key4=value4
+     * @param key the key of extra value
+     * @return the value of the specified key
+     */
+    public String getExtraValueOf(String key) {
+        return getExtraValueHashMap().get(key);
+    }
+
     @Override
     public int hashCode() {
         return mSubtypeHashCode;
index 874eac8..7d8289a 100644 (file)
@@ -1175,6 +1175,8 @@ public class WebView extends AbsoluteLayout
 
         mOverscrollDistance = configuration.getScaledOverscrollDistance();
         mOverflingDistance = configuration.getScaledOverflingDistance();
+
+        setScrollBarStyle(super.getScrollBarStyle());
     }
 
     /**
index efbcd58..08c8144 100644 (file)
@@ -805,8 +805,11 @@ class ZoomManager {
             if (mWebView.getWebViewCore() != null) {
                 // we always force, in case our height changed, in which case we
                 // still want to send the notification over to webkit.
-                setZoomScale(Math.max(mActualScale, getZoomOverviewScale()),
-                    mUpdateTextWrap, true);
+                // Keep overview mode unchanged when rotating.
+                final float zoomOverviewScale = getZoomOverviewScale();
+                final float newScale = (mInZoomOverview) ?
+                    zoomOverviewScale : Math.max(mActualScale, zoomOverviewScale); 
+                setZoomScale(newScale, mUpdateTextWrap, true);
                 // update the zoom buttons as the scale can be changed
                 updateZoomPicker();
             }
@@ -846,22 +849,29 @@ class ZoomManager {
     public void onNewPicture(WebViewCore.DrawData drawData) {
         final int viewWidth = mWebView.getViewWidth();
         final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
+        final float newZoomOverviewScale = getZoomOverviewScale();
         WebSettings settings = mWebView.getSettings();
         if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() &&
             settings.getUseFixedViewport() &&
             (mInitialZoomOverview || mInZoomOverview)) {
-            mTextWrapScale = getReadingLevelScale();
+            // Keep mobile site's text wrap scale unchanged.  For mobile sites,
+            // the text wrap scale is the same as zoom overview scale, which is 1.0f.
+            if (exceedsMinScaleIncrement(mTextWrapScale, 1.0f) ||
+                    exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f)) {
+                mTextWrapScale = getReadingLevelScale();
+            } else {
+                mTextWrapScale = newZoomOverviewScale;
+            }
         }
 
-        final float zoomOverviewScale = getZoomOverviewScale();
         if (!mMinZoomScaleFixed) {
-            mMinZoomScale = zoomOverviewScale;
+            mMinZoomScale = newZoomOverviewScale;
         }
         // fit the content width to the current view. Ignore the rounding error case.
         if (!mWebView.drawHistory() && (mInitialZoomOverview || (mInZoomOverview
                 && Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) {
             mInitialZoomOverview = false;
-            setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
+            setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
                 !mWebView.getSettings().getUseFixedViewport());
         }
     }
index 3f38f2e..27020c5 100644 (file)
@@ -334,6 +334,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
      * the drawing cache was enabled on the children
      */
     boolean mCachingStarted;
+    boolean mCachingActive;
 
     /**
      * The position of the view that received the down motion event
@@ -4169,7 +4170,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         if (mScrollingCacheEnabled && !mCachingStarted) {
             setChildrenDrawnWithCacheEnabled(true);
             setChildrenDrawingCacheEnabled(true);
-            mCachingStarted = true;
+            mCachingStarted = mCachingActive = true;
         }
     }
 
@@ -4178,7 +4179,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
             mClearScrollingCache = new Runnable() {
                 public void run() {
                     if (mCachingStarted) {
-                        mCachingStarted = false;
+                        mCachingStarted = mCachingActive = false;
                         setChildrenDrawnWithCacheEnabled(false);
                         if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
                             setChildrenDrawingCacheEnabled(false);
index 8811492..560fc68 100644 (file)
@@ -604,6 +604,7 @@ public class ListPopupWindow {
         removePromptView();
         mPopup.setContentView(null);
         mDropDownList = null;
+        mHandler.removeCallbacks(mResizePopupRunnable);
     }
 
     /**
index 12a0ebf..2802144 100644 (file)
@@ -3013,12 +3013,9 @@ public class ListView extends AbsListView {
         return mItemsCanFocus;
     }
 
-    /**
-     * @hide Pending API council approval.
-     */
     @Override
     public boolean isOpaque() {
-        return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque &&
+        return (mCachingActive && mIsCacheColorOpaque && mDividerIsOpaque &&
                 hasOpaqueScrollbars()) || super.isOpaque();
     }
 
@@ -3071,6 +3068,10 @@ public class ListView extends AbsListView {
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        if (mCachingStarted) {
+            mCachingActive = true;
+        }
+
         // Draw the dividers
         final int dividerHeight = mDividerHeight;
         final Drawable overscrollHeader = mOverScrollHeader;
@@ -3164,7 +3165,6 @@ public class ListView extends AbsListView {
                 }
             } else {
                 int top;
-                int listTop = effectivePaddingTop;
 
                 final int scrollY = mScrollY;
 
@@ -3181,7 +3181,7 @@ public class ListView extends AbsListView {
                         View child = getChildAt(i);
                         top = child.getTop();
                         // Don't draw dividers next to items that are not enabled
-                        if (top > listTop) {
+                        if (top > effectivePaddingTop) {
                             if ((areAllItemsSelectable ||
                                     (adapter.isEnabled(first + i) && (i == count - 1 ||
                                             adapter.isEnabled(first + i + 1))))) {
@@ -3220,6 +3220,15 @@ public class ListView extends AbsListView {
         super.dispatchDraw(canvas);
     }
 
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        boolean more = super.drawChild(canvas, child, drawingTime);
+        if (mCachingActive && child.mCachingFailed) {
+            mCachingActive = false;
+        }
+        return more;
+    }
+
     /**
      * Draws a divider for the given child in the given bounds.
      *
@@ -3558,6 +3567,7 @@ public class ListView extends AbsListView {
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        //noinspection SimplifiableIfStatement
         if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
             // Don't handle edge touches immediately -- they may actually belong to one of our
             // descendants.
index f051b77..22edcd0 100644 (file)
@@ -394,7 +394,6 @@ public class SearchView extends LinearLayout {
         if (mIconifiedByDefault == iconified) return;
         mIconifiedByDefault = iconified;
         updateViewsVisibility(iconified);
-        setImeVisibility(!iconified);
     }
 
     /**
index 28b106b..993af31 100644 (file)
@@ -304,15 +304,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     }
     InputMethodState mInputMethodState;
 
-    int mTextSelectHandleLeftRes;
-    int mTextSelectHandleRightRes;
-    int mTextSelectHandleRes;
-    int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
-    int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
+    private int mTextSelectHandleLeftRes;
+    private int mTextSelectHandleRightRes;
+    private int mTextSelectHandleRes;
+    private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
+    private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
 
-    Drawable mSelectHandleLeft;
-    Drawable mSelectHandleRight;
-    Drawable mSelectHandleCenter;
+    private int mCursorDrawableRes;
+    private final Drawable[] mCursorDrawable = new Drawable[2];
+    private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
+
+    private Drawable mSelectHandleLeft;
+    private Drawable mSelectHandleRight;
+    private Drawable mSelectHandleCenter;
 
     private int mLastDownPositionX, mLastDownPositionY;
     private Callback mCustomSelectionActionModeCallback;
@@ -742,6 +746,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
                 break;
 
+            case com.android.internal.R.styleable.TextView_textCursorDrawable:
+                mCursorDrawableRes = a.getResourceId(attr, 0);
+                break;
+
             case com.android.internal.R.styleable.TextView_textSelectHandleLeft:
                 mTextSelectHandleLeftRes = a.getResourceId(attr, 0);
                 break;
@@ -3770,33 +3778,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         if (mHighlightPathBogus) {
             invalidateCursor();
         } else {
-            synchronized (sTempRect) {
-                /*
-                 * The reason for this concern about the thickness of the
-                 * cursor and doing the floor/ceil on the coordinates is that
-                 * some EditTexts (notably textfields in the Browser) have
-                 * anti-aliased text where not all the characters are
-                 * necessarily at integer-multiple locations.  This should
-                 * make sure the entire cursor gets invalidated instead of
-                 * sometimes missing half a pixel.
-                 */
-
-                float thick = FloatMath.ceil(mTextPaint.getStrokeWidth());
-                if (thick < 1.0f) {
-                    thick = 1.0f;
-                }
+            final int horizontalPadding = getCompoundPaddingLeft();
+            final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
 
-                thick /= 2;
+            if (mCursorCount == 0) {
+                synchronized (sTempRect) {
+                    /*
+                     * The reason for this concern about the thickness of the
+                     * cursor and doing the floor/ceil on the coordinates is that
+                     * some EditTexts (notably textfields in the Browser) have
+                     * anti-aliased text where not all the characters are
+                     * necessarily at integer-multiple locations.  This should
+                     * make sure the entire cursor gets invalidated instead of
+                     * sometimes missing half a pixel.
+                     */
+                    float thick = FloatMath.ceil(mTextPaint.getStrokeWidth());
+                    if (thick < 1.0f) {
+                        thick = 1.0f;
+                    }
 
-                mHighlightPath.computeBounds(sTempRect, false);
+                    thick /= 2.0f;
 
-                int left = getCompoundPaddingLeft();
-                int top = getExtendedPaddingTop() + getVerticalOffset(true);
+                    mHighlightPath.computeBounds(sTempRect, false);
 
-                invalidate((int) FloatMath.floor(left + sTempRect.left - thick),
-                           (int) FloatMath.floor(top + sTempRect.top - thick),
-                           (int) FloatMath.ceil(left + sTempRect.right + thick),
-                           (int) FloatMath.ceil(top + sTempRect.bottom + thick));
+                    invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick),
+                            (int) FloatMath.floor(verticalPadding + sTempRect.top - thick),
+                            (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick),
+                            (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick));
+                }
+            } else {
+                for (int i = 0; i < mCursorCount; i++) {
+                    Rect bounds = mCursorDrawable[i].getBounds();
+                    invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
+                            bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
+                }
             }
         }
     }
@@ -3836,13 +3851,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     line2 = mLayout.getLineForOffset(last);
 
                 int bottom = mLayout.getLineTop(line2 + 1);
-                int voffset = getVerticalOffset(true);
 
-                int left = getCompoundPaddingLeft() + mScrollX;
-                invalidate(left, top + voffset + getExtendedPaddingTop(),
-                           left + getWidth() - getCompoundPaddingLeft() -
-                           getCompoundPaddingRight(),
-                           bottom + voffset + getExtendedPaddingTop());
+                final int horizontalPadding = getCompoundPaddingLeft();
+                final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
+                
+                // If used, the cursor drawables can have an arbitrary dimension that can go beyond
+                // the invalidated lines specified above.
+                for (int i = 0; i < mCursorCount; i++) {
+                    Rect bounds = mCursorDrawable[i].getBounds();
+                    top = Math.min(top, bounds.top);
+                    bottom = Math.max(bottom, bounds.bottom);
+                    // Horizontal bounds are already full width, no need to update
+                }
+
+                invalidate(horizontalPadding + mScrollX, top + verticalPadding,
+                        horizontalPadding + mScrollX + getWidth() -
+                        getCompoundPaddingLeft() - getCompoundPaddingRight(),
+                        bottom + verticalPadding);
             }
         }
     }
@@ -4346,6 +4371,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
         Path highlight = null;
         int selStart = -1, selEnd = -1;
+        boolean drawCursor = false;
 
         //  If there is no movement method, then there can be no selection.
         //  Check that first and attempt to skip everything having to do with
@@ -4366,6 +4392,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         if (mHighlightPathBogus) {
                             mHighlightPath.reset();
                             mLayout.getCursorPath(selStart, mHighlightPath, mText);
+                            updateCursorsPositions();
                             mHighlightPathBogus = false;
                         }
 
@@ -4377,7 +4404,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         }
                         mHighlightPaint.setStyle(Paint.Style.STROKE);
 
-                        highlight = mHighlightPath;
+                        if (mCursorCount > 0) {
+                            drawCursor = true;
+                        } else {
+                            highlight = mHighlightPath;
+                        }
                     }
                 } else {
                     if (mHighlightPathBogus) {
@@ -4460,6 +4491,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
         }
 
+        if (drawCursor) drawCursor(canvas, cursorOffsetVertical);
+
         layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
 
         if (mMarquee != null && mMarquee.shouldDrawGhost()) {
@@ -4478,6 +4511,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         updateCursorControllerPositions();
     }
 
+    private void updateCursorsPositions() {
+        if (mCursorDrawableRes == 0) return;
+
+        final int offset = getSelectionStart();
+        final int line = mLayout.getLineForOffset(offset);
+        final int top = mLayout.getLineTop(line);
+        final int bottom = mLayout.getLineTop(line + 1);
+
+        mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
+
+        int middle = bottom;
+        if (mCursorCount == 2) {
+            // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
+            middle = (top + bottom) >> 1;
+        }
+
+        updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
+
+        if (mCursorCount == 2) {
+            updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
+        }
+    }
+
+    private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
+        if (mCursorDrawable[cursorIndex] == null)
+            mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
+
+        if (mTempRect == null) mTempRect = new Rect();
+
+        mCursorDrawable[cursorIndex].getPadding(mTempRect);
+        final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
+        horizontal = Math.max(0.5f, horizontal - 0.5f);
+        final int left = (int) (horizontal) - mTempRect.left;
+        mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+                bottom + mTempRect.bottom);
+    }
+
+    private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
+        final boolean translate = cursorOffsetVertical != 0;
+        if (translate) canvas.translate(0, cursorOffsetVertical);
+        for (int i = 0; i < mCursorCount; i++) {
+            mCursorDrawable[i].draw(canvas);
+        }
+        if (translate) canvas.translate(0, -cursorOffsetVertical);
+    }
+
     /**
      * Update the positions of the CursorControllers.  Needed by WebTextView,
      * which does not draw.
@@ -8699,7 +8778,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
                 mDrawable = mSelectHandleLeft;
                 handleWidth = mDrawable.getIntrinsicWidth();
-                mHotspotX = (handleWidth * 3) / 4;
+                mHotspotX = handleWidth * 3.0f / 4.0f;
                 break;
             }
 
@@ -8710,7 +8789,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
                 mDrawable = mSelectHandleRight;
                 handleWidth = mDrawable.getIntrinsicWidth();
-                mHotspotX = handleWidth / 4;
+                mHotspotX = handleWidth / 4.0f;
                 break;
             }
 
@@ -8722,7 +8801,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
                 mDrawable = mSelectHandleCenter;
                 handleWidth = mDrawable.getIntrinsicWidth();
-                mHotspotX = handleWidth / 2;
+                mHotspotX = handleWidth / 2.0f;
                 mIsInsertionHandle = true;
                 break;
             }
@@ -8937,8 +9016,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             final int lineBottom = mLayout.getLineBottom(line);
 
             final Rect bounds = sCursorControllerTempRect;
-            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - mHotspotX)
-                + TextView.this.mScrollX;
+            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) +
+                    TextView.this.mScrollX;
             bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY;
 
             bounds.right = bounds.left + width;
index 1f93eac..6c9e7bb 100644 (file)
@@ -101,8 +101,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
         }
 
         if (anchor != null) {
-            mTreeObserver = anchor.getViewTreeObserver();
-            mTreeObserver.addOnGlobalLayoutListener(this);
+            if (mTreeObserver == null) {
+                mTreeObserver = anchor.getViewTreeObserver();
+                mTreeObserver.addOnGlobalLayoutListener(this);
+            }
             mPopup.setAnchorView(anchor);
         } else {
             return false;
@@ -123,10 +125,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
 
     public void onDismiss() {
         mPopup = null;
-        if (mTreeObserver != null) {
-            mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this);
-            mTreeObserver = null;
+        if (mTreeObserver != null && mTreeObserver.isAlive()) {
+            mTreeObserver.removeGlobalOnLayoutListener(this);
         }
+        mTreeObserver = null;
     }
 
     public boolean isShowing() {
@@ -134,6 +136,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
     }
 
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        if (!isShowing()) return;
+
         MenuItem item = null;
         if (mOverflowOnly) {
             item = mMenu.getOverflowItem(position);
@@ -184,13 +188,15 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
     @Override
     public void onGlobalLayout() {
         if (!isShowing()) {
-            mTreeObserver.removeGlobalOnLayoutListener(this);
+            if (mTreeObserver.isAlive()) {
+                mTreeObserver.removeGlobalOnLayoutListener(this);
+            }
             mTreeObserver = null;
         } else {
             final View anchor = mAnchorView != null ? mAnchorView.get() : null;
-            if (anchor != null && !anchor.isShown()) {
+            if (anchor == null || !anchor.isShown()) {
                 dismiss();
-            } else {
+            } else if (isShowing()) {
                 // Recompute window size and position
                 mPopup.show();
             }
index 2c39871..bf0504f 100644 (file)
@@ -1214,6 +1214,45 @@ static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object,
     return JNI_FALSE;
 }
 
+static jboolean disconnectPanServerDeviceNative(JNIEnv *env, jobject object,
+                                                jstring path, jstring address,
+                                                jstring iface) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    LOGE("disconnectPanServerDeviceNative");
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_address = env->GetStringUTFChars(address, NULL);
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        const char *c_iface = env->GetStringUTFChars(iface, NULL);
+
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1,
+                                        onPanDeviceConnectionResult,
+                                        context_path, eventLoopNat,
+                                        get_adapter_path(env, object),
+                                        DBUS_NETWORKSERVER_IFACE,
+                                        "DisconnectDevice",
+                                        DBUS_TYPE_STRING, &c_address,
+                                        DBUS_TYPE_STRING, &c_iface,
+                                        DBUS_TYPE_INVALID);
+
+        env->ReleaseStringUTFChars(address, c_address);
+        env->ReleaseStringUTFChars(iface, c_iface);
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
@@ -1274,6 +1313,8 @@ static JNINativeMethod sMethods[] = {
     {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
               (void *)connectPanDeviceNative},
     {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
+    {"disconnectPanServerDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+              (void *)disconnectPanServerDeviceNative},
 };
 
 
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
new file mode 100644 (file)
index 0000000..b9435b6
Binary files /dev/null and b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
new file mode 100644 (file)
index 0000000..477d820
Binary files /dev/null and b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png differ
index 8802003..6f37dc0 100755 (executable)
     <!-- Color of link text (URLs). -->
     <attr name="textColorLink" format="reference|color" />
 
+    <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
+    <attr name="textCursorDrawable" format="reference" />
+
     <!-- Indicates that the content of a non-editable TextView can be selected.
      Default value is false. EditText content is always selectable. -->
     <attr name="textIsSelectable" format="boolean" />
         <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
         <attr name="textEditSideNoPasteWindowLayout" />
 
+        <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
+        <attr name="textCursorDrawable" />
+
         <!-- Indicates that the content of a non-editable text can be selected. -->
         <attr name="textIsSelectable" />
     </declare-styleable>
index aaf071b..4542575 100644 (file)
   <!-- Default icon for applications that don't specify an icon. -->
   <public type="mipmap" name="sym_def_app_icon" id="0x010d0000" />
 
+  <!--  Theme attribute used to customize the text insertion cursor -->
+  <!--  Commented out for HC MR1 to prevent an API change -->
+  <!--  <public type="attr" name="textCursorDrawable" id="0x01010362" /> -->
 </resources>
index 5700641..8cc5944 100644 (file)
         <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
         <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item>
         <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item>
+        <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item>
     </style>
     
     <style name="Widget.TextView.ListSeparator">
index 6d5b482..927a668 100644 (file)
         <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
         <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item>
         <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item>
+        <item name="textCursorDrawable">@null</item>
 
         <!-- Widget styles -->
         <item name="absListViewStyle">@android:style/Widget.AbsListView</item>
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item>
 
         <!-- Widget styles -->
         <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item>
 
         <!-- Widget styles -->
         <item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item>
index 9b02d34..7d4a2d1 100644 (file)
@@ -7,162 +7,498 @@ page.title=Debugging and Profiling User Interfaces
 
       <ol>
         <li>
-          <a href="#hierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy
-          Viewer</a>
-          <ol>
-            <li><a href="#layoutview">Layout View</a></li>
-            <li><a href="#pixelperfect">Pixel Perfect View</a></li>
-          </ol>       
+            <a href="#HierarchyViewer">
+                Debugging and Optimizing User Interfaces with Hierarchy Viewer
+            </a>
+            <ol>
+                <li><a href="#runhv">Running Hierarchy Viewer and choosing a window</a></li>
+                <li><a href="#viewhierarchy">About the View Hierarchy window</a></li>
+                <li><a href="#indiView">Working with an individual View in Tree View</a></li>
+                <li><a href="#hvdebugging">Debugging with View Hierarchy</a></li>
+                <li><a href="#hvoptimize">Optimizing with View Hierarchy</a></li>
+            </ol>
+        </li>
+        <li>
+            <a href="#pixelperfect">
+                Examining and Designing User Interfaces with Pixel Perfect
+            </a>
+            <ol>
+                <li><a href="#aboutpixelperfect">About the Pixel Perfect window</a></li>
+                <li><a href="#overlays">Working with Pixel Perfect overlays</a></li>
+            </ol>
         </li>
-
         <li><a href="#layoutopt">Optimizing Layouts with <code>layoutopt</code></a></li>
       </ol>
     </div>
   </div>
 
-  <p>Sometimes your application's layout can slow down your application. 
-  To help debug issues in your layout, the Android SDK provides the Hierarchy Viewer and 
+  <p>Sometimes your application's layout can slow down your application.
+  To help debug issues in your layout, the Android SDK provides the Hierarchy Viewer and
   <code>layoutopt</code> tools.
   </p>
-  
+
   <p>The Hierarchy Viewer application allows you to debug and optimize your user interface. It
-  provides a visual representation of the layout's View hierarchy (the Layout View) and a magnified
-  inspector of the display (the Pixel Perfect View).</p>
+  provides a visual representation of the layout's View hierarchy (the View Hierarchy window)
+  and a magnified view of the display (the Pixel Perfect window).</p>
 
   <p><code>layoutopt</code> is a command-line tool that helps you optimize the layouts and layout
   hierarchies of your applications. You can run it against your layout files or resource
   directories to quickly check for inefficiencies or other types of problems that could be
   affecting the performance of your application.</p>
 
-  <h2 id="hierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy Viewer</h2>
-
-  <p>To get the Hierarchy Viewer started:</p>
-
-  <ol>
-    <li>Connect your device or launch an emulator.</li>
-
-    <li>From a terminal, launch <code>hierarchyviewer</code> from the <code>&lt;sdk&gt;/tools/</code>
-    directory.</li>
-
-    <li>In the window that opens, you'll see a list of <strong>Devices</strong>. When a device is
-    selected, a list of currently active <strong>Windows</strong> is displayed on the right. The
-    <em>&lt;Focused Window&gt;</em> is the window currently in the foreground, and also the default
-    window loaded if you do not select another.</li>
-
-    <li>Select the window that you'd like to inspect and click <strong>Load View
-    Hierarchy</strong>. The Layout View will be loaded. You can then load the Pixel Perfect View by
-    clicking the second icon at the bottom-left of the window.</li>
-  </ol>
-
-  <p>If you've navigated to a different window on the device, press <strong>Refresh
-  Windows</strong> to refresh the list of available windows on the right.</p>
-
-
-  <h3 id="layoutview">Layout View</h3>
-
-  <p>The Layout View offers a look at the View layout and properties. It has three views:</p>
-
-  <ul>
-    <li>Tree View: a hierarchy diagram of the Views, on the left.</li>
-
-    <li>Properties View: a list of the selected View's properties, on the top-right.</li>
-
-    <li>Wire-frame View: a wire-frame drawing of the layout, on the bottom-right.</li>
-  </ul><br />
-  <img src="{@docRoot}images/hierarchyviewer-layout.png"
-        alt=""
-        height="509"
-        width="700" />
-  <p class="img-caption"><strong>Figure 1.</strong> Screenshot of Hierarchy Viewer</p>
-
-  <p>Select a node in the Tree View to display the properties of that element in the Properties
-  View. When a node is selected, the Wire-frame View also indicates the bounds of the element with
-  a red rectangle. Double click a node in the tree (or select it, and click <strong>Display
-  View</strong>) to open a new window with a rendering of that element.</p>
-
-  <p>The Layout View includes a couple other helpful features for debugging your layout:
-  <strong>Invalidate</strong> and <strong>Request Layout</strong>. These buttons execute the
-  respective View calls, {@link android.view.View#invalidate()} and {@link
-  android.view.View#requestLayout()}, on the View element currently selected in the tree. Calling
-  these methods on any View can be very useful when simultaneously running a debugger on your
-  application.</p>
-
-  <p>The Tree View can be resized by adjusting the zoom slider, below the diagram. The number of
-  View elements in the window is also given here. You should look for ways to minimize the number
-  of Views. The fewer View elements there are in a window, the faster it will perform.</p>
+<h2 id="HierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy Viewer</h2>
 
-  <p>If you interact with the device and change the focused View, the diagram will not
-  automatically refresh. You must reload the Layout View by clicking <strong>Load View
-  Hierarchy</strong>.</p>
-
-  <h3 id="pixelperfect">Pixel Perfect View</h3>
-
-  <p>The Pixel Perfect View provides a magnified look at the current device window. It helps you
-  design your UI better by giving you a closer look at your UI's image quality, alignment, and other
-  aesthetic qualities. It has three views:</p>
-
-  <ul>
-    <li>Explorer View: shows the View hierarchy as a list, on the left.</li>
-
-    <li>Normal View: a normal view of the device window, in the middle.</li>
-
-    <li>Loupe View: a magnified, pixel-grid view of the device window, on the right.</li>
-  </ul><br />
-  <img src="{@docRoot}images/hierarchyviewer-pixelperfect.png"
+<h3 id="runhv">Running Hierarchy Viewer and choosing a window</h3>
+<p>
+    To run Hierarchy Viewer, follow these steps:</p>
+<ol>
+    <li>
+        Connect your device or launch an emulator.
+        <p>
+            To preserve security, Hierarchy Viewer can only connect to devices running a
+            developer version of the Android system.
+        </p>
+    </li>
+    <li>
+        If you have not done so already, install the application you want to work with.
+    </li>
+    <li>
+        Run the application, and ensure that its UI is visible.
+    </li>
+    <li>
+        From a terminal, launch <code>hierarchyviewer</code> from the
+        <code>&lt;sdk&gt;/tools/</code>
+        directory.
+    </li>
+    <li>
+        The first window you see displays a list of devices and emulators. To expand the list
+        of Activity objects for a device or emulator, click the arrow on the left. This displays a
+        list of the Activity objects whose UI is currently visible on the device or emulator. The
+        objects are listed by their Android component name. The list includes both your application
+        Activity and system Activity objects. A screenshot of this window appears in
+        figure 1.
+    </li>
+    <li>
+        Select the name of your Activity from the list. You can now look at its view
+        hierarchy using the View Hierarchy window, or look at a magnified image of the UI using
+        the Pixel Perfect window.
+    </li>
+</ol>
+<p>
+    To learn how to use the View Hierarchy window, go to
+    <a href="#viewhierarchy">About the View Hierarchy window</a>. To learn how to use the
+    Pixel Perfect window, go to <a href="#pixelperfect">About the Pixel Perfect window</a>.
+</p>
+<img id="Fig1" src="{@docRoot}images/developing/hv_device_window.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 1.</strong> Hierarchy Viewer device window</p>
+<h3 id="viewhierarchy">About the View Hierarchy window</h3>
+<p>
+    The View Hierarchy window displays the View objects that form the UI of the
+    Activity that is running on your device or emulator. You use it to look at individual
+    View objects within the context of the entire View tree. For each View object, the View
+    Hierarchy window also displays rendering performance data.
+</p>
+<p>
+    To see the View Hierarchy window, run Hierarchy Viewer as described in
+    the section <a href="#runhv">Running Hierarchy Viewer and choosing a window</a>. Next, click
+    <strong>View Hierarchy</strong> at the top of the device window.
+</p>
+<p>
+    You should see four panes:
+</p>
+<ul>
+    <li>
+        <strong>Tree View</strong>: The left-hand pane displays the Tree View,
+        a diagram of the Activity object's hierarchy of views. Use Tree View to examine individual
+        View objects and see the relationships between View objects in your UI.
+        <p>
+            To zoom in on the pane, use the slider at the bottom of the pane, or use your mouse
+            scroll wheel. To move around in the pane or reveal View objects that are not currently
+            visible, click and drag the pane.
+        </p>
+        <p>
+            To highlight the nodes in the tree whose class or ID match a search string, enter the
+            string in the <strong>Filter by class or id:</strong> edit box at the bottom of the
+            window. The background of nodes that match the search string will change from gray to
+            bright blue.
+        </p>
+        <p>
+            To save a screenshot of Tree View to a PNG file, click <strong>Save As PNG</strong> at
+            the top of the View Hierarchy window. This displays a dialog in which you can choose
+            a directory and file name.
+        </p>
+        <p>
+            To save a layered screenshot of your device or emulator to an Adobe Photoshop (PSD)
+            file, click <strong>Capture Layers</strong> at the top of the View Hierarchy window.
+            This displays a dialog in which you can choose a directory or file name.
+            Each View in the UI is saved as a separate Photoshop layer.
+        </p>
+        <p>
+            In Photoshop (or similar program that accepts .psd files), you can hide, show or edit a
+            layer independently of others. When you save a layered screenshot, you can examine and
+            modify the image of an individual View object. This helps you experiment with design
+            changes.
+        </p>
+    </li>
+    <li>
+        The upper right-hand pane displays the <strong>Tree Overview</strong>, a smaller map
+        representation of the entire Tree View window. Use Tree Overview to identify the part of the
+        view tree that is being displayed in Tree View.
+        <p>
+            You can also use Tree Overview to move around in the Tree View pane. Click and drag
+            the shaded rectangle over an area to reveal it in Tree View.
+        </p>
+    </li>
+    <li>
+        The middle right-hand pane displays the <strong>Properties View</strong>,
+        a list of the properties for a selected View object. With Properties View, you can
+        examine all the properties without having to look at your application source.
+        <p>
+            The properties are organized by category. To find an individual property, expand
+            a category name by clicking the arrow on its left. This reveals all the properties
+            in that category.
+        </p>
+    </li>
+    <li>
+        The lower right-hand pane displays the <strong>Layout View</strong>,
+        a block representation of the UI. Layout View is another way to navigate through your UI.
+        When you click on a View object in Tree View, its position in the UI is highlighted.
+        Conversely, when you click in an area of Layout View, the View object for that area is
+        highlighted in Tree View.
+        <p>
+            The outline colors of blocks in Layout View provide additional information:
+        </p>
+            <ul>
+                <li>
+                    Bold red: The block represents the the View that is currently selected in
+                    Tree View.
+                </li>
+                <li>
+                    Light red: The block represents the parent of the block outlined in bold red.
+                </li>
+                <li>
+                    White: The block represents a visible View that is not a parent or child of the
+                    View that is currently selected in Tree View.
+                </li>
+            </ul>
+    </li>
+</ul>
+<p>
+    When the UI of the current Activity changes, the View Hierarchy window is not automatically
+    updated. To update it, click <strong>Load View Hierarchy</strong> at the top of the window.
+</p>
+<p>
+    Also, the window is not updated if you switch to a new Activity. To update it, start by
+    clicking the window selection icon in the bottom left-hand corner of the window. This
+    navigates back to the Window Selection window. From this window, click the Android
+    component name of the new Activity and then click <strong>Load View Hierarchy</strong>
+    at the top of the window.
+</p>
+<p>
+    A screenshot of the View Hierarchy window appears in figure 2.
+</p>
+<img id="Fig2" src="{@docRoot}images/developing/hv_view_hierarchy_window.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 2.</strong> The View Hierarchy window</p>
+<h3 id="indiView">Working with an individual View in Tree View</h3>
+<p>
+    Each node in Tree View represents a single View. Some information is always visible. Starting
+    at the top of the node, you see the following:
+</p>
+<ol>
+    <li>
+        View class: The View object's class.
+    </li>
+    <li>
+        View object address: A pointer to View object.
+    </li>
+    <li>
+        View object ID: The value of the
+        <code><a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">android:id</a>
+        </code> attribute.
+    </li>
+    <li>
+        Performance indicators: A set of three colored dots that indicate the rendering
+        speed of this View relative to other View objects in the tree. The three dots
+        represent (from left to right) the measure, layout, and draw times of the rendering.
+        <p>
+            The colors indicate the following relative performance:
+        </p>
+        <ul>
+            <li>
+                Green: For this part of the render time, this View is in the faster 50% of all
+                the View objects in the tree. For example, a green dot for the measure time means
+                that this View has a faster measure time than 50% of the View objects in the tree.
+            </li>
+            <li>
+                Yellow: For this part of the render time, this View is in the slower 50% of all
+                the View objects in the tree. For example, a yellow dot for the layout time means
+                that this View has a slower layout time than 50% of the View objects in the tree.
+            </li>
+            <li>
+                Red: For this part of the render time, this View is the slowest one in the tree.
+                For example, a red dot for the draw time means that this View takes the most
+                time to draw of all the View objects in the tree.
+            </li>
+        </ul>
+    </li>
+    <li>
+        View index: The zero-based index of the View in its parent View. If it is the only child,
+        this is 0.
+    </li>
+</ol>
+<p>
+    When you select a node, additional information for the View appears in a small window above
+    the node. When you click one of the nodes, you see the following:
+</p>
+<ul>
+    <li>
+        Image: The actual image of the View, as it would appear in the emulator. If the View has
+        children, these are also displayed.
+    </li>
+    <li>
+        View count: The number of View objects represented by this node. This includes the View
+        itself and a count of its children. For example, this value is 4 for a View that has 3
+        children.
+    </li>
+    <li>
+        Render times: The actual measure, layout, and draw times for the View rendering, in
+        milliseconds. These represent the same values as the performance indicators mentioned in
+        the preceding section.
+    </li>
+</ul>
+<p>
+    An annotated screenshot of an individual node in the Tree View window appears in figure 3.
+</p>
+<img id="Fig3" src="{@docRoot}images/developing/hv_treeview_screenshot.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 3.</strong> An annotated node in Tree View</p>
+<h3 id="hvdebugging">Debugging with View Hierarchy</h3>
+<p>
+    The View Hierarchy window helps you debug an application by providing a static display
+    of the UI. The display starts with your application's opening screen. As you step through
+    your application, the display remains unchanged until you redraw it by invalidating and
+    then requesting layout for a View.
+</p>
+<p>
+    To redraw a View in the display:
+</p>
+    <ul>
+        <li>
+            Select a View in Tree View. As you move up towards the root of the tree (to the
+            left in the Tree View), you see the highest-level View objects. Redrawing a high-level
+            object usually forces the lower-level objects to redraw as well.
+        </li>
+        <li>
+            Click <strong>Invalidate</strong> at the top of the window. This marks the View as
+            invalid, and schedules it for a redraw at the next point that a layout is requested.
+        </li>
+        <li>
+            Click <strong>Request Layout</strong> to request a layout. The View and its children
+            are redrawn, as well as any other View objects that need to be redrawn.
+        </li>
+    </ul>
+<p>
+    Manually redrawing a View allows you to watch the View object tree and examine the properties of
+    individual View objects one step at a time as you go through breakpoints in your code.
+</p>
+<h3 id="hvoptimize">Optimizing with View Hierarchy</h3>
+<p>
+    View Hierarchy also helps you identify slow render performance. You start by looking at the
+    View nodes with red or yellow performance indicators to identify the slower View objects. As you
+    step through your application, you can judge if a View is consistently slow or slow only in
+    certain circumstances.
+</p>
+<p>
+    Remember that slow performance is not necessarily evidence of a problem, especially for
+    ViewGroup objects. View objects that have more children and more complex View objects render
+    more slowly.
+</p>
+<p>
+    The View Hierarchy window also helps you find performance issues. Just by looking at the
+    performance indicators (the dots) for each View node, you can see which View objects are the
+    slowest to measure, layout, and draw. From that, you can quickly identify the problems you
+    should look at first.
+</p>
+<h2 id="pixelperfect">Examining and Designing User Interfaces with Pixel Perfect</h2>
+<p>
+    Pixel Perfect is a tool for examining pixel properties and laying out UIs from a design drawing.
+</p>
+<h3 id="aboutpixelperfect">About the Pixel Perfect window</h3>
+<p>
+    The Pixel Perfect window displays a magnified image of the screen that is currently
+    visible on the emulator or device. In it, you can examine the properties
+    of individual pixels in the screen image. You can also use the Pixel Perfect window
+    to help you lay out your application UI based on a bitmap design.
+</p>
+<p>
+    To see the Pixel Perfect window, run Hierarchy Viewer, as described in
+    the section <a href="#runhv">Running Hierarchy Viewer and choosing a window</a>. Next, click
+    <strong>Inspect Screenshot</strong> at the top of the device window. The Pixel Perfect window
+    appears.
+</p>
+<p>
+    In it, you see three panes:
+</p>
+<ul>
+    <li>
+        View Object pane: This is a hierarchical list of the View objects that are currently
+        visible on the device or emulator screen, including both the ones in your application and
+        the ones generated by the system. The objects are listed by their View class.
+        To see the class names of a View object's children, expand the View by clicking the
+        arrow to its left. When you click a View, its position is highlighted in the Pixel Perfect
+        pane on the right.
+    </li>
+    <li>
+        Pixel Perfect Loupe pane: This is the magnified screen image. It is overlaid by a grid in
+        which each square represents one pixel. To look at the information for a pixel, click in its
+        square. Its color and X,Y coordinates appear at the bottom of the pane.
+        <p>
+            The magenta crosshair in the pane corresponds to the positioning
+            crosshair in the next pane. It only moves when you move the crosshair in the next pane.
+        </p>
+        <p>
+            To zoom in or out on the image, use the <strong>Zoom</strong> slider at the bottom of
+            the pane, or use your mouse's scroll wheel.
+        </p>
+        <p>
+            When you select a pixel in the Loupe pane, you see the following information at the
+            bottom of the pane:
+        </p>
+        <ul>
+            <li>
+                Pixel swatch: A rectangle filled with the same color as the pixel.
+            </li>
+            <li>
+                HTML color code: The hexadecimal RGB code corresponding to the pixel color
+            </li>
+            <li>
+                RGB color values: A list of the (R), green (G), and blue (B) color values of the
+                pixel color. Each value is in the range 0-255.
+            </li>
+            <li>
+                X and Y coordinates: The pixel's coordinates, in device-specific pixel units.
+                The values are 0-based, with X=0 at the left of the screen and Y=0 at the top.
+            </li>
+        </ul>
+    </li>
+    <li>
+        Pixel Perfect pane: This displays the currently visible screen as it would appear in the
+        emulator.
+        <p>
+            You use the cyan crosshair to do coarse positioning. Drag the crosshair in the image,
+            and the Loupe crosshair will move accordingly. You can also click on a point in the
+            Pixel Perfect pane, and the crosshair will move to that point.
+        </p>
+        <p>
+            The image corresponding to the View object selected in the View Object pane is
+            outlined in a box that indicates the View object's position on the screen. For the
+            selected object, the box is bold red. Sibling and parent View objects have a light
+            red box. View objects that are neither parents nor siblings are in white.
+        </p>
+        <p>
+            The layout box may have other rectangles either inside or outside it, each of which
+            indicates part of the View. A purple or green rectangle indicates the View bounding box.
+            A white or black box inside the layout box represents the <strong>padding</strong>, the
+            defined distance between the View object's content and its bounding box. An outer white
+            or black rectangle represents the <strong>margins</strong>, the distance between the
+            View bounding box and adjacent View objects. The padding and margin boxes are white if
+            the layout background is black, and vice versa.
+        </p>
+        <p>
+            You can save the screen image being displayed in the Pixel Perfect pane as a PNG file.
+            This produces a screenshot of the current screen. To do this, click
+            <strong>Save as PNG</strong> at the top of the window. This displays a dialog,
+            in which you can choose a directory and filename for the file.
+        </p>
+    </li>
+</ul>
+<p>
+    The panes are not automatically refreshed when you change one of the View objects or go to
+    another Activity. To refresh the Pixel Perfect pane and the Loupe pane, click
+    <strong>Refresh Screenshot</strong> at the top of the window. This will change the panes
+    to reflect the current screen image. You still may need to refresh the View Object pane;
+    to do this, click <strong>Refresh Tree</strong> at the top of the window.
+</p>
+<p>
+    To automatically refresh the panes while you are debugging, set
+    <strong>Auto Refresh</strong> at the top of the window, and then set a refresh rate
+    with the <strong>Refresh Rate</strong> slider at the bottom of the Loupe pane.
+</p>
+<h3 id="overlays">Working with Pixel Perfect overlays</h3>
+<p>
+    You often construct a UI based on a design done as a bitmap image. The Pixel Perfect window
+    helps you match up your View layout to a bitmap image by allowing you to load the bitmap as an
+    <strong>overlay</strong> on the screen image.
+</p>
+<p>
+    To use a bitmap image as an overlay:
+</p>
+<ul>
+    <li>
+        Start your application in a device or emulator and navigate to the Activity whose UI you
+        want to work with.
+    </li>
+    <li>
+        Start Hierarchy Viewer and navigate to the Pixel Perfect window.
+    </li>
+    <li>
+        At the top of the window, click <strong>Load Overlay</strong>. A dialog opens, prompting
+        for the image file to load. Load the image file.
+    </li>
+    <li>
+        Pixel Perfect displays the overlay over the screen image in the Pixel Perfect pane. The
+        lower left corner of the bitmap image (X=0, Y=<em>max value</em>) is anchored on the lower
+        leftmost pixel (X=0, Y=<em>max screen</em>) of the screen.
+        <p>
+            By default, the overlay has a 50% transparency, which allows you to see the screen
+            image underneath. You can adjust this with the <strong>Overlay:</strong> slider at the
+            bottom of the Loupe pane.
+        </p>
+        <p>
+            Also by default, the overlay is not displayed in the Loupe pane. To display it,
+            set <strong>Show in Loupe</strong> at the top of the window.
+        </p>
+    </li>
+</ul>
+<p>
+    The overlay is not saved as part of the screenshot when you save the screen image as a PNG
+    file.
+</p>
+<p>
+    A screenshot of the Pixel Perfect window appears in figure 4.
+</p>
+<img id="Fig4" src="{@docRoot}images/developing/hv_pixelperfect.png"
         alt=""
-        height="509"
-        width="700" />
-
-  <p>Click on an element in the Explorer View and a "layout box" will be drawn in the Normal View
-  to indicate the layout position of that element. The layout box uses multiple rectangles, to
-  indicate the normal bounds, the padding and the margin (as needed). The purple or green rectangle
-  indicates the normal bounds of the element (the height and width). The inner white or black
-  rectangle indicates the content bounds, when padding is present. A black or white rectangle
-  outside the normal purple/green rectangle indicates any present margins. (There are two colors
-  for each rectangle, in order to provide the best contrast based on the colors currently in the
-  background.)</p>
-
-  <p>A very handy feature for designing your UI is the ability to overlay an image in the Normal
-  and Loupe Views. For example, you might have a mock-up image of how you'd like to layout your
-  interface. By selecting <strong>Load...</strong> from the controls in the Normal View, you can
-  choose the image from your computer and it will be placed atop the preview. Your chosen image
-  will anchor at the bottom left corner of the screen. You can then adjust the opacity of the
-  overlay and begin fine-tuning your layout to match the mock-up.</p>
-
-  <p>The Normal View and Loupe View refresh at regular intervals (5 seconds by default), but the
-  Explorer View does not. If you navigate away and focus on a different View, then you should
-  refresh the Explorer's hierarchy by clicking <strong>Load View Hierarchy</strong>. This is even
-  true when you're working in a window that holds multiple Views that are not always visible. If
-  you do not, although the previews will refresh, clicking a View in the Explorer will not provide
-  the proper layout box in the Normal View, because the hierarchy believes you are still focused on
-  the prior View.</p>
-
-  <p>Optional controls include:</p>
-
-  <ul>
-    <li><strong>Overlay</strong>: Load an overlay image onto the view and adjust its opacity.</li>
-
-    <li><strong>Refresh Rate</strong>: Adjust how often the Normal and Loupe View refresh their
-    display.</li>
-
-    <li><strong>Zoom</strong>: Adjust the zoom level of the Loupe View.</li>
-  </ul>
-
-  <h2 id="layoutopt">Optimizing layouts with layoutopt</h2>
-  <p>The <code>layoutopt</code> tool lets you analyze the XML files that represent your application's layout
-  and finds ineffiencies in the view hierarchy.</p>
-  
-  <p>To run the tool, open a terminal and launch <code>layoutopt &lt;resources&gt;</code> from your
-  SDK <code>tools/</code> directory. In the command, supply a list of uncompiled resource xml files
-  or directories that you want to analyze.</p>
-
-  <p>When run, the tool loads the specified XML files and analyzes their layout structures and
-  hierarchies according to a set of predefined rules. If it detects issues, it outputs information
-  about the issues, giving filename, line numbers, description of issue, and for some types of
-  issues a suggested resolution.</p>
+        height="600"/>
+<p class="img-caption"><strong>Figure 4.</strong> The Pixel Perfect window</p>
+<h2 id="layoutopt">Optimizing layouts with layoutopt</h2>
+<p>
+    The <code>layoutopt</code> tool lets you analyze the XML files that define your
+    application's UI to find inefficiencies in the view hierarchy.</p>
 
-  <p>Here's an example of the output:</p>
-  <pre>
+<p>
+    To run the tool, open a terminal and launch <code>layoutopt &lt;xmlfiles&gt;</code>
+    from your SDK <code>tools/</code> directory. The &lt;xmlfiles&gt; argument is a space-
+    delimited list of resources you want to analyze, either uncompiled resource xml files or
+    directories of such files.
+</p>
+<p>
+    The tool loads the specified XML files and analyzes their definitions and
+    hierarchies according to a set of predefined rules. For every issue it detects, it
+    displays the following information:
+</p>
+<ul>
+    <li>
+        The filename in which the issue was detected.
+    </li>
+    <li>
+        The line number for the issue.
+    </li>
+    <li>
+        A description of the issue, and for some types of issues it also suggests a resolution.
+    </li>
+</ul>
+<p>The following is a sample of the output from the tool:</p>
+<pre>
 $ layoutopt samples/
 samples/compound.xml
    7:23 The root-level &lt;FrameLayout/&gt; can be replaced with &lt;merge/&gt;
@@ -188,15 +524,3 @@ samples/useless.xml
    7:19 The root-level &lt;FrameLayout/&gt; can be replaced with &lt;merge/&gt;
    11:17 This LinearLayout layout or its FrameLayout parent is useless
 </pre>
-
-<p>
-For more information on running the tool, see the
-<a href="${@docRoot}guide/developing/debugging/debugging-ui.html#layoutopt">layoutopt</a> reference.</p>
-  
-    
-
-
-
-
-
-
diff --git a/docs/html/images/developing/hv_device_window.png b/docs/html/images/developing/hv_device_window.png
new file mode 100644 (file)
index 0000000..2bb80a8
Binary files /dev/null and b/docs/html/images/developing/hv_device_window.png differ
diff --git a/docs/html/images/developing/hv_pixelperfect.png b/docs/html/images/developing/hv_pixelperfect.png
new file mode 100644 (file)
index 0000000..6d19119
Binary files /dev/null and b/docs/html/images/developing/hv_pixelperfect.png differ
diff --git a/docs/html/images/developing/hv_treeview_screenshot.png b/docs/html/images/developing/hv_treeview_screenshot.png
new file mode 100644 (file)
index 0000000..c0e7ac5
Binary files /dev/null and b/docs/html/images/developing/hv_treeview_screenshot.png differ
diff --git a/docs/html/images/developing/hv_view_hierarchy_window.png b/docs/html/images/developing/hv_view_hierarchy_window.png
new file mode 100644 (file)
index 0000000..0d8b263
Binary files /dev/null and b/docs/html/images/developing/hv_view_hierarchy_window.png differ
index febdb9a..5839064 100644 (file)
@@ -569,7 +569,7 @@ var ANDROID_RESOURCES = [
     tags: ['sample', 'new', 'newfeature', 'widgets'],
     path: 'samples/StackWidget/index.html',
     title: {
-      en: 'StackView App Widget'
+      en: 'StackView Widget'
     },
     description: {
       en: 'Demonstrates how to create a simple collection widget containing a StackView.'
@@ -619,7 +619,7 @@ var ANDROID_RESOURCES = [
     tags: ['sample', 'widgets', 'newfeature', 'new'],
     path: 'samples/WeatherListWidget/index.html',
     title: {
-      en: 'Weather List Widget Sample'
+      en: 'Weather List Widget'
     },
     description: {
       en: 'A more complex collection-widget example which uses a ContentProvider as its data source.'
index 1f1f6d3..136bcd9 100644 (file)
@@ -131,7 +131,10 @@ android.app.FragmentTransaction#commit commit()} and the system applies the frag
 the activity.</p>
 
 <p>For more information about using fragments, read the <a
-href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> documentation.</p>
+href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> documentation. Several
+samples are also available in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">
+API Demos</a> application.</p>
 
 
 
@@ -181,7 +184,10 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
 android:targetSdkVersion}</a> to {@code "11"}.</p>
 
 <p>For more information about the Action Bar, read the <a
-href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> documentation.</p>
+href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> documentation. Several
+samples are also available in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#ActionBar">
+API Demos</a> application.</p>
 
 
 
@@ -221,9 +227,11 @@ before attempting to paste it.</p>
 object) at a time, but one {@link android.content.ClipData} can contain multiple {@link
 android.content.ClipData.Item}s.</p>
 
-<p>For more information, see the {@link android.content.ClipData} class reference. You can also see
-an example implementation of copy and paste in the <a
-href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sample application.</p>
+<p>For more information, read the <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy
+and Paste</a> documentation. You can also see a simple implementation of copy and paste in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/content/ClipboardSample.
+html">API Demos</a> and a more complete implementation in the <a
+href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> application.</p>
 
 
 
@@ -265,6 +273,13 @@ getAction()} on the {@link android.view.DragEvent}.</p>
 android.content.ClipData} object, this is not related to the system clipboard. A drag and drop
 operation should never put the dragged data in the system clipboard.</p>
 
+<p>For more information, read the <a href="{@docRoot}guide/topics/ui/drag-drop.html">Dragging and
+Dropping</a> documentation. You can also see an implementation of drag and drop in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html">
+API Demos</a> application and the <a
+href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a>
+application.</p>
+
 
 
 <h3>App widgets</h3>
@@ -279,8 +294,8 @@ widgets with collections, using widgets such as {@link android.widget.GridView},
 android.widget.ListView}, and {@link android.widget.StackView} that are backed by remote data,
 such as from a content provider.</p>
 
-<p>The {@link android.appwidget.AppWidgetProviderInfo} class (defined with an {@code
-&lt;appwidget-provider&gt; XML file) also supports two new fields: {@link
+<p>The {@link android.appwidget.AppWidgetProviderInfo} class (defined in XML with an {@code
+&lt;appwidget-provider&gt;} element) also supports two new fields: {@link
 android.appwidget.AppWidgetProviderInfo#autoAdvanceViewId} and {@link
 android.appwidget.AppWidgetProviderInfo#previewImage}. The {@link
 android.appwidget.AppWidgetProviderInfo#autoAdvanceViewId} field lets you specify the view ID of the
@@ -295,7 +310,10 @@ application called "Widget Preview." To create a preview image, launch this appl
 app widget for your application and set it up how you'd like your preview image to appear, then save
 it and place it in your application's drawable resources.</p>
 
-
+<p>You can see an implementation of the new app widget features in the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView App Widget</a> and <a
+href="{@docRoot}resources/samples/WeatherListWidget/index.html">Weather List Widget</a>
+applications.</p>
 
 
 
@@ -319,7 +337,6 @@ notification can control music playback without starting an activity.</li>
 
 
 
-
 <h3>Content loaders</h3>
 
 <p>New framework APIs facilitate asynchronous loading of data using the {@link
@@ -333,6 +350,13 @@ LoaderCallbacks} interface to receive callbacks when a new loader is requested o
 changed, then call {@link android.app.LoaderManager#initLoader initLoader()} to initialize the
 loader for your activity or fragment.</p>
 
+<p>For more information, read the <a
+href="{@docRoot}guide/topics/providers/loaders.html">Loaders</a> documentation. You can also see
+example code using loaders in the <a
+href="{@docRoot}samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.html">
+FragmentListCursorLoader</a> and <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
+LoaderThrottle</a> samples.</p>
 
 
 
@@ -395,7 +419,10 @@ such as a {@link android.animation.ValueAnimator} or {@link android.animation.Ob
 discussed above.</p>
 
 <p>For more information, see the <a
-href="{@docRoot}guide/topics/graphics/animation.html">Animation</a> developer guide.</p>
+href="{@docRoot}guide/topics/graphics/animation.html">Property Animation</a> documentation. You can
+also see several samples using the animation APIs in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
+Demos</a> application.</p>
 
 
 
@@ -582,7 +609,10 @@ android.view.View#LAYER_TYPE_SOFTWARE} documentation.</p>
 <p>Renderscript is a runtime 3D framework that provides both an API for building 3D scenes as well
 as a special, platform-independent shader language for maximum performance. Using Renderscript, you
 can accelerate graphics operations and data processing. Renderscript is an ideal way to create
-high-performance 3D effects for applications, wallpapers, carousels, and more.</p></li>
+high-performance 3D effects for applications, wallpapers, carousels, and more.</p>
+<p>For more information, see the <a
+href="{@docRoot}guide/topics/graphics/renderscript.html">3D Rendering and Computation with
+Renderscript</a> documentation.</p></li>
 </ul>
 
 
index bd903da..b2f4379 100644 (file)
@@ -245,25 +245,80 @@ public final class Bitmap implements Parcelable {
         }
     }
 
+    /**
+     * Possible bitmap configurations. A bitmap configuration describes
+     * how pixels are stored. This affects the quality (color depth) as
+     * well as the ability to display transparent/translucent colors.
+     */
     public enum Config {
         // these native values must match up with the enum in SkBitmap.h
+
+        /**
+         * Each pixel is stored as a single translucency (alpha) channel.
+         * This is very useful to efficiently store masks for instance.
+         * No color information is stored.
+         * With this configuration, each pixel requires 1 byte of memory.
+         */
         ALPHA_8     (2),
+
+        /**
+         * Each pixel is stored on 2 bytes and only the RGB channels are
+         * encoded: red is stored with 5 bits of precision (32 possible
+         * values), green is stored with 6 bits of precision (64 possible
+         * values) and blue is stored with 5 bits of precision.
+         * 
+         * This configuration can produce slight visual artifacts depending
+         * on the configuration of the source. For instance, without
+         * dithering, the result might show a greenish tint. To get better
+         * results dithering should be applied.
+         * 
+         * This configuration may be useful when using opaque bitmaps
+         * that do not require high color fidelity.
+         */
         RGB_565     (4),
+
+        /**
+         * Each pixel is stored on 2 bytes. The three RGB color channels
+         * and the alpha channel (translucency) are stored with a 4 bits
+         * precision (16 possible values.)
+         * 
+         * This configuration is mostly useful if the application needs
+         * to store translucency information but also needs to save
+         * memory.
+         * 
+         * It is recommended to use {@link #ARGB_8888} instead of this
+         * configuration.
+         * 
+         * @deprecated Because of the poor quality of this configuration,
+         *             it is advised to use {@link #ARGB_8888} instead.
+         */
+        @Deprecated
         ARGB_4444   (5),
+
+        /**
+         * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
+         * for translucency) is stored with 8 bits of precision (256
+         * possible values.)
+         * 
+         * This configuration is very flexible and offers the best
+         * quality. It should be used whenever possible.
+         */
         ARGB_8888   (6);
 
+        final int nativeInt;
+
+        @SuppressWarnings({"deprecation"})
+        private static Config sConfigs[] = {
+            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
+        };
+        
         Config(int ni) {
             this.nativeInt = ni;
         }
-        final int nativeInt;
 
-        /* package */ static Config nativeToConfig(int ni) {
+        static Config nativeToConfig(int ni) {
             return sConfigs[ni];
         }
-
-        private static Config sConfigs[] = {
-            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
-        };
     }
 
     /**
@@ -473,6 +528,7 @@ public final class Bitmap implements Parcelable {
                 case ALPHA_8:
                     newConfig = Config.ALPHA_8;
                     break;
+                //noinspection deprecation
                 case ARGB_4444:
                 case ARGB_8888:
                 default:
index 8214e7f..8c78d60 100644 (file)
@@ -786,92 +786,92 @@ class MediaArtistNativeHelper {
 
     /** Defines video profiles and levels. */
     public final class VideoProfile {
-        /** MPEG4, Simple Profile, Level 0. */
-        public static final int MPEG4_SP_LEVEL_0 = 0;
-
-        /** MPEG4, Simple Profile, Level 0B. */
-        public static final int MPEG4_SP_LEVEL_0B = 1;
-
-        /** MPEG4, Simple Profile, Level 1. */
-        public static final int MPEG4_SP_LEVEL_1 = 2;
-
-        /** MPEG4, Simple Profile, Level 2. */
-        public static final int MPEG4_SP_LEVEL_2 = 3;
-
-        /** MPEG4, Simple Profile, Level 3. */
-        public static final int MPEG4_SP_LEVEL_3 = 4;
-
         /** H263, Profile 0, Level 10. */
-        public static final int H263_PROFILE_0_LEVEL_10 = 5;
+        public static final int H263_PROFILE_0_LEVEL_10 = MediaProperties.H263_PROFILE_0_LEVEL_10;
 
         /** H263, Profile 0, Level 20. */
-        public static final int H263_PROFILE_0_LEVEL_20 = 6;
+        public static final int H263_PROFILE_0_LEVEL_20 = MediaProperties.H263_PROFILE_0_LEVEL_20;
 
         /** H263, Profile 0, Level 30. */
-        public static final int H263_PROFILE_0_LEVEL_30 = 7;
+        public static final int H263_PROFILE_0_LEVEL_30 = MediaProperties.H263_PROFILE_0_LEVEL_30;
 
         /** H263, Profile 0, Level 40. */
-        public static final int H263_PROFILE_0_LEVEL_40 = 8;
+        public static final int H263_PROFILE_0_LEVEL_40 = MediaProperties.H263_PROFILE_0_LEVEL_40;
 
         /** H263, Profile 0, Level 45. */
-        public static final int H263_PROFILE_0_LEVEL_45 = 9;
+        public static final int H263_PROFILE_0_LEVEL_45 = MediaProperties.H263_PROFILE_0_LEVEL_45;
+
+        /** MPEG4, Simple Profile, Level 0. */
+        public static final int MPEG4_SP_LEVEL_0 = MediaProperties.MPEG4_SP_LEVEL_0;
+
+        /** MPEG4, Simple Profile, Level 0B. */
+        public static final int MPEG4_SP_LEVEL_0B = MediaProperties.MPEG4_SP_LEVEL_0B;
+
+        /** MPEG4, Simple Profile, Level 1. */
+        public static final int MPEG4_SP_LEVEL_1 = MediaProperties.MPEG4_SP_LEVEL_1;
+
+        /** MPEG4, Simple Profile, Level 2. */
+        public static final int MPEG4_SP_LEVEL_2 = MediaProperties.MPEG4_SP_LEVEL_2;
+
+        /** MPEG4, Simple Profile, Level 3. */
+        public static final int MPEG4_SP_LEVEL_3 = MediaProperties.MPEG4_SP_LEVEL_3;
 
         /** MPEG4, Simple Profile, Level 4A. */
-        public static final int MPEG4_SP_LEVEL_4A = 10;
+        public static final int MPEG4_SP_LEVEL_4A = MediaProperties.MPEG4_SP_LEVEL_4A;
 
         /** MPEG4, Simple Profile, Level 0. */
-        public static final int MPEG4_SP_LEVEL_5 = 11;
+        public static final int MPEG4_SP_LEVEL_5 = MediaProperties.MPEG4_SP_LEVEL_5;
 
         /** H264, Profile 0, Level 1. */
-        public static final int H264_PROFILE_0_LEVEL_1 = 12;
+        public static final int H264_PROFILE_0_LEVEL_1 = MediaProperties.H264_PROFILE_0_LEVEL_1;
 
         /** H264, Profile 0, Level 1b. */
-        public static final int H264_PROFILE_0_LEVEL_1b = 13;
+        public static final int H264_PROFILE_0_LEVEL_1b = MediaProperties.H264_PROFILE_0_LEVEL_1B;
 
         /** H264, Profile 0, Level 1.1 */
-        public static final int H264_PROFILE_0_LEVEL_1_1 = 14;
+        public static final int H264_PROFILE_0_LEVEL_1_1 = MediaProperties.H264_PROFILE_0_LEVEL_1_1;
 
         /** H264, Profile 0, Level 1.2 */
-        public static final int H264_PROFILE_0_LEVEL_1_2 = 15;
+        public static final int H264_PROFILE_0_LEVEL_1_2 = MediaProperties.H264_PROFILE_0_LEVEL_1_2;
 
         /** H264, Profile 0, Level 1.3 */
-        public static final int H264_PROFILE_0_LEVEL_1_3 = 16;
+        public static final int H264_PROFILE_0_LEVEL_1_3 = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
 
         /** H264, Profile 0, Level 2. */
-        public static final int H264_PROFILE_0_LEVEL_2 = 17;
+        public static final int H264_PROFILE_0_LEVEL_2 = MediaProperties.H264_PROFILE_0_LEVEL_2;
 
         /** H264, Profile 0, Level 2.1 */
-        public static final int H264_PROFILE_0_LEVEL_2_1 = 18;
+        public static final int H264_PROFILE_0_LEVEL_2_1 = MediaProperties.H264_PROFILE_0_LEVEL_2_1;
 
         /** H264, Profile 0, Level 2.2 */
-        public static final int H264_PROFILE_0_LEVEL_2_2 = 19;
+        public static final int H264_PROFILE_0_LEVEL_2_2 = MediaProperties.H264_PROFILE_0_LEVEL_2_2;
 
         /** H264, Profile 0, Level 3. */
-        public static final int H264_PROFILE_0_LEVEL_3 = 20;
+        public static final int H264_PROFILE_0_LEVEL_3 = MediaProperties.H264_PROFILE_0_LEVEL_3;
 
         /** H264, Profile 0, Level 3.1 */
-        public static final int H264_PROFILE_0_LEVEL_3_1 = 21;
+        public static final int H264_PROFILE_0_LEVEL_3_1 = MediaProperties.H264_PROFILE_0_LEVEL_3_1;
 
         /** H264, Profile 0, Level 3.2 */
-        public static final int H264_PROFILE_0_LEVEL_3_2 = 22;
+        public static final int H264_PROFILE_0_LEVEL_3_2 = MediaProperties.H264_PROFILE_0_LEVEL_3_2;
 
         /** H264, Profile 0, Level 4. */
-        public static final int H264_PROFILE_0_LEVEL_4 = 23;
+        public static final int H264_PROFILE_0_LEVEL_4 = MediaProperties.H264_PROFILE_0_LEVEL_4;
 
         /** H264, Profile 0, Level 4.1 */
-        public static final int H264_PROFILE_0_LEVEL_4_1 = 24;
+        public static final int H264_PROFILE_0_LEVEL_4_1 = MediaProperties.H264_PROFILE_0_LEVEL_4_1;
 
         /** H264, Profile 0, Level 4.2 */
-        public static final int H264_PROFILE_0_LEVEL_4_2 = 25;
+        public static final int H264_PROFILE_0_LEVEL_4_2 = MediaProperties.H264_PROFILE_0_LEVEL_4_2;
 
         /** H264, Profile 0, Level 5. */
-        public static final int H264_PROFILE_0_LEVEL_5 = 26;
+        public static final int H264_PROFILE_0_LEVEL_5 = MediaProperties.H264_PROFILE_0_LEVEL_5;
 
         /** H264, Profile 0, Level 5.1 */
-        public static final int H264_PROFILE_0_LEVEL_5_1 = 27;
+        public static final int H264_PROFILE_0_LEVEL_5_1 = MediaProperties.H264_PROFILE_0_LEVEL_5_1;
 
         /** Profile out of range. */
-        public static final int OUT_OF_RANGE = 255;
+        public static final int OUT_OF_RANGE = MediaProperties.UNSUPPORTED_PROFILE_LEVEL;
     }
 
     /** Defines video frame sizes. */
index e368848..cb08023 100644 (file)
@@ -399,6 +399,9 @@ void AwesomePlayer::reset_l() {
         if (mConnectingDataSource != NULL) {
             LOGI("interrupting the connection process");
             mConnectingDataSource->disconnect();
+        } else if (mConnectingRTSPController != NULL) {
+            LOGI("interrupting the connection process");
+            mConnectingRTSPController->disconnect();
         }
 
         if (mFlags & PREPARING_CONNECTED) {
@@ -409,7 +412,7 @@ void AwesomePlayer::reset_l() {
     }
 
     if (mFlags & PREPARING) {
-        LOGI("waiting until preparation is completes.");
+        LOGI("waiting until preparation is completed.");
     }
 
     while (mFlags & PREPARING) {
@@ -1633,7 +1636,13 @@ status_t AwesomePlayer::finishSetDataSource_l() {
             mLooper->start();
         }
         mRTSPController = new ARTSPController(mLooper);
+        mConnectingRTSPController = mRTSPController;
+
+        mLock.unlock();
         status_t err = mRTSPController->connect(mUri.string());
+        mLock.lock();
+
+        mConnectingRTSPController.clear();
 
         LOGI("ARTSPController::connect returned %d", err);
 
index 797e5ca..98b8c05 100644 (file)
@@ -205,6 +205,7 @@ private:
 
     sp<ALooper> mLooper;
     sp<ARTSPController> mRTSPController;
+    sp<ARTSPController> mConnectingRTSPController;
 
     sp<LiveSession> mLiveSession;
 
index 13988cd..9f6bd29 100644 (file)
@@ -104,7 +104,7 @@ AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
       mNextExpectedSeqNoValid(false),
       mNextExpectedSeqNo(0),
       mAccessUnitDamaged(false) {
-    mIsGeneric = desc.startsWith("mpeg4-generic/");
+    mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14);
 
     if (mIsGeneric) {
         AString value;
index f0b858d..679dcab 100644 (file)
@@ -637,7 +637,7 @@ APacketSource::APacketSource(
 
         mFormat->setInt32(kKeyWidth, width);
         mFormat->setInt32(kKeyHeight, height);
-    } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+    } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
         AString val;
         if (!GetAttribute(params.c_str(), "mode", &val)
                 || (strcasecmp(val.c_str(), "AAC-lbr")
index 601f569..47de4e0 100644 (file)
@@ -123,7 +123,7 @@ void ARTPConnection::MakePortPair(
         struct sockaddr_in addr;
         memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
         addr.sin_family = AF_INET;
-        addr.sin_addr.s_addr = INADDR_ANY;
+        addr.sin_addr.s_addr = htonl(INADDR_ANY);
         addr.sin_port = htons(port);
 
         if (bind(*rtpSocket,
@@ -340,6 +340,8 @@ void ARTPConnection::onPollStreams() {
 }
 
 status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
+    LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP");
+
     CHECK(!s->mIsInjected);
 
     sp<ABuffer> buffer = new ABuffer(65536);
index 893a387..84c666f 100644 (file)
@@ -67,7 +67,7 @@ ARTPSource::ARTPSource(
     } else  if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
         mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
     } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
-            || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+            || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
         mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
         mIssueFIRRequests = true;
     } else {
index a7563ff..1328d2e 100644 (file)
@@ -69,7 +69,14 @@ status_t ARTSPController::connect(const char *url) {
 void ARTSPController::disconnect() {
     Mutex::Autolock autoLock(mLock);
 
-    if (mState != CONNECTED) {
+    if (mState == CONNECTING) {
+        mState = DISCONNECTED;
+        mConnectionResult = ERROR_IO;
+        mCondition.broadcast();
+
+        mHandler.clear();
+        return;
+    } else if (mState != CONNECTED) {
         return;
     }
 
index 3e710dc..f03f7a2 100644 (file)
@@ -71,6 +71,11 @@ bool ASessionDescription::parse(const void *data, size_t size) {
             line.setTo(desc, i, eolPos - i);
         }
 
+        if (line.empty()) {
+            i = eolPos + 1;
+            continue;
+        }
+
         if (line.size() < 2 || line.c_str()[1] != '=') {
             return false;
         }
index ba7c1b2..d15d9c5 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <arpa/inet.h>
 #include <sys/socket.h>
+#include <netdb.h>
 
 // If no access units are received within 5 secs, assume that the rtp
 // stream has ended and signal end of stream.
@@ -121,9 +122,10 @@ struct MyHandler : public AHandler {
         // want to transmit user/pass in cleartext.
         AString host, path, user, pass;
         unsigned port;
-        if (ARTSPConnection::ParseURL(
-                    mSessionURL.c_str(), &host, &port, &path, &user, &pass)
-                && user.size() > 0) {
+        CHECK(ARTSPConnection::ParseURL(
+                    mSessionURL.c_str(), &host, &port, &path, &user, &pass));
+
+        if (user.size() > 0) {
             mSessionURL.clear();
             mSessionURL.append("rtsp://");
             mSessionURL.append(host);
@@ -133,6 +135,8 @@ struct MyHandler : public AHandler {
 
             LOGI("rewritten session url: '%s'", mSessionURL.c_str());
         }
+
+        mSessionHost = host;
     }
 
     void connect(const sp<AMessage> &doneMsg) {
@@ -248,34 +252,64 @@ struct MyHandler : public AHandler {
     // In case we're behind NAT, fire off two UDP packets to the remote
     // rtp/rtcp ports to poke a hole into the firewall for future incoming
     // packets. We're going to send an RR/SDES RTCP packet to both of them.
-    void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+    bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+        struct sockaddr_in addr;
+        memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+        addr.sin_family = AF_INET;
+
         AString source;
         AString server_port;
         if (!GetAttribute(transport.c_str(),
                           "source",
-                          &source)
-                || !GetAttribute(transport.c_str(),
+                          &source)) {
+            LOGW("Missing 'source' field in Transport response. Using "
+                 "RTSP endpoint address.");
+
+            struct hostent *ent = gethostbyname(mSessionHost.c_str());
+            if (ent == NULL) {
+                LOGE("Failed to look up address of session host '%s'",
+                     mSessionHost.c_str());
+
+                return false;
+            }
+
+            addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+        } else {
+            addr.sin_addr.s_addr = inet_addr(source.c_str());
+        }
+
+        if (!GetAttribute(transport.c_str(),
                                  "server_port",
                                  &server_port)) {
-            return;
+            LOGI("Missing 'server_port' field in Transport response.");
+            return false;
         }
 
         int rtpPort, rtcpPort;
         if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
                 || rtpPort <= 0 || rtpPort > 65535
                 || rtcpPort <=0 || rtcpPort > 65535
-                || rtcpPort != rtpPort + 1
-                || (rtpPort & 1) != 0) {
-            return;
+                || rtcpPort != rtpPort + 1) {
+            LOGE("Server picked invalid RTP/RTCP port pair %s,"
+                 " RTP port must be even, RTCP port must be one higher.",
+                 server_port.c_str());
+
+            return false;
         }
 
-        struct sockaddr_in addr;
-        memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
-        addr.sin_family = AF_INET;
-        addr.sin_addr.s_addr = inet_addr(source.c_str());
+        if (rtpPort & 1) {
+            LOGW("Server picked an odd RTP port, it should've picked an "
+                 "even one, we'll let it pass for now, but this may break "
+                 "in the future.");
+        }
 
         if (addr.sin_addr.s_addr == INADDR_NONE) {
-            return;
+            return true;
+        }
+
+        if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) {
+            // No firewalls to traverse on the loopback interface.
+            return true;
         }
 
         // Make up an RR/SDES RTCP packet.
@@ -289,16 +323,26 @@ struct MyHandler : public AHandler {
         ssize_t n = sendto(
                 rtpSocket, buf->data(), buf->size(), 0,
                 (const sockaddr *)&addr, sizeof(addr));
-        CHECK_EQ(n, (ssize_t)buf->size());
+
+        if (n < (ssize_t)buf->size()) {
+            LOGE("failed to poke a hole for RTP packets");
+            return false;
+        }
 
         addr.sin_port = htons(rtcpPort);
 
         n = sendto(
                 rtcpSocket, buf->data(), buf->size(), 0,
                 (const sockaddr *)&addr, sizeof(addr));
-        CHECK_EQ(n, (ssize_t)buf->size());
+
+        if (n < (ssize_t)buf->size()) {
+            LOGE("failed to poke a hole for RTCP packets");
+            return false;
+        }
 
         LOGV("successfully poked holes.");
+
+        return true;
     }
 
     virtual void onMessageReceived(const sp<AMessage> &msg) {
@@ -381,6 +425,7 @@ struct MyHandler : public AHandler {
                                 response->mContent->size());
 
                         if (!mSessionDesc->isValid()) {
+                            LOGE("Failed to parse session description.");
                             result = ERROR_MALFORMED;
                         } else {
                             ssize_t i = response->mHeaders.indexOfKey("content-base");
@@ -395,6 +440,25 @@ struct MyHandler : public AHandler {
                                 }
                             }
 
+                            if (!mBaseURL.startsWith("rtsp://")) {
+                                // Some misbehaving servers specify a relative
+                                // URL in one of the locations above, combine
+                                // it with the absolute session URL to get
+                                // something usable...
+
+                                LOGW("Server specified a non-absolute base URL"
+                                     ", combining it with the session URL to "
+                                     "get something usable...");
+
+                                AString tmp;
+                                CHECK(MakeURL(
+                                            mSessionURL.c_str(),
+                                            mBaseURL.c_str(),
+                                            &tmp));
+
+                                mBaseURL = tmp;
+                            }
+
                             CHECK_GT(mSessionDesc->countTracks(), 1u);
                             setupTrack(1);
                         }
@@ -455,9 +519,12 @@ struct MyHandler : public AHandler {
                         if (!track->mUsingInterleavedTCP) {
                             AString transport = response->mHeaders.valueAt(i);
 
-                            pokeAHole(track->mRTPSocket,
-                                      track->mRTCPSocket,
-                                      transport);
+                            // We are going to continue even if we were
+                            // unable to poke a hole into the firewall...
+                            pokeAHole(
+                                    track->mRTPSocket,
+                                    track->mRTCPSocket,
+                                    transport);
                         }
 
                         mRTPConn->addStream(
@@ -853,17 +920,16 @@ struct MyHandler : public AHandler {
             case 'tiou':
             {
                 if (!mReceivedFirstRTCPPacket) {
-                    if (mTryFakeRTCP) {
-                        LOGW("Never received any data, disconnecting.");
-                        (new AMessage('abor', id()))->post();
-                    } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) {
+                    if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
                         LOGW("We received RTP packets but no RTCP packets, "
                              "using fake timestamps.");
 
                         mTryFakeRTCP = true;
 
                         mReceivedFirstRTCPPacket = true;
-                    } else {
+
+                        fakeTimestamps();
+                    } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
                         LOGW("Never received any data, switching transports.");
 
                         mTryTCPInterleaving = true;
@@ -871,6 +937,9 @@ struct MyHandler : public AHandler {
                         sp<AMessage> msg = new AMessage('abor', id());
                         msg->setInt32("reconnect", true);
                         msg->post();
+                    } else {
+                        LOGW("Never received any data, disconnecting.");
+                        (new AMessage('abor', id()))->post();
                     }
                 }
                 break;
@@ -1016,6 +1085,7 @@ private:
     sp<ASessionDescription> mSessionDesc;
     AString mOriginalSessionURL;  // This one still has user:pass@
     AString mSessionURL;
+    AString mSessionHost;
     AString mBaseURL;
     AString mSessionID;
     bool mSetupTracksSuccessful;
@@ -1158,6 +1228,12 @@ private:
         return true;
     }
 
+    void fakeTimestamps() {
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            onTimeUpdate(i, 0, 0ll);
+        }
+    }
+
     void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
         LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
              trackIndex, rtpTime, ntpTime);
index fb1b073..4ea8849 100644 (file)
@@ -38,6 +38,7 @@
 
 namespace android {
 
+#if 0
 static bool isMtpDevice(uint16_t vendor, uint16_t product) {
     // Sandisk Sansa Fuze
     if (vendor == 0x0781 && product == 0x74c2)
@@ -47,6 +48,7 @@ static bool isMtpDevice(uint16_t vendor, uint16_t product) {
         return true;
     return false;
 }
+#endif
 
 MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
     struct usb_device *device = usb_device_new(deviceName, fd);
@@ -91,7 +93,9 @@ MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
                 LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
                 free(manufacturerName);
                 free(productName);
-            } else {
+            }
+#if 0
+             else {
                 // look for special cased devices based on vendor/product ID
                 // we are doing this mainly for testing purposes
                 uint16_t vendor = usb_device_get_vendor_id(device);
@@ -119,7 +123,7 @@ MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
                     printf("no MTP string\n");
                 }
             }
-
+#endif
             // if we got here, then we have a likely MTP or PTP device
 
             // interface should be followed by three endpoints
index a72c283..4999e5d 100644 (file)
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2011 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.
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
-  android:layout_width="match_parent" 
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">
 
   <FrameLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    
+
   <SurfaceView
      android:id="@+id/surface_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_centerInParent="true"
      />
-     
-  <VideoView 
-   android:id="@+id/video_view" 
+
+  <ImageView android:id="@+id/overlay_layer"
+     android:layout_width="0dip"
+     android:layout_height="392dip"/>
+
+  <VideoView
+   android:id="@+id/video_view"
         android:layout_width="320px"
         android:layout_height="240px"
   />
-  
+
   </FrameLayout>
-    
+
 </LinearLayout>
 
index 9fb49b1..41f0e22 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -40,6 +40,9 @@ import android.widget.MediaController;
 import android.widget.VideoView;
 import com.android.mediaframeworktest.MediaNames;
 
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.net.InetAddress;
@@ -58,6 +61,8 @@ public class MediaFrameworkTest extends Activity {
     public static AssetFileDescriptor midiafd;
     public static AssetFileDescriptor mp3afd;
     
+    public static Bitmap mDestBitmap;
+    public static ImageView mOverlayView;
     
     public MediaFrameworkTest() {
     }
@@ -69,6 +74,7 @@ public class MediaFrameworkTest extends Activity {
         super.onCreate(icicle);
         setContentView(R.layout.surface_view);
         mSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
+        mOverlayView = (ImageView)findViewById(R.id.overlay_layer);
         ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
         mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
         
@@ -77,6 +83,9 @@ public class MediaFrameworkTest extends Activity {
         
         //Get the mp3 fd
         mp3afd = this.getResources().openRawResourceFd(R.raw.testmp3);
+        mOverlayView.setLayoutParams(lp);
+        mDestBitmap = Bitmap.createBitmap((int)640, (int)480, Bitmap.Config.ARGB_8888);
+        mOverlayView.setImageBitmap(mDestBitmap);
     }
     
     public void startPlayback(String filename){
@@ -148,4 +157,9 @@ public class MediaFrameworkTest extends Activity {
       InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
       return address.isReachable(10000);
   }
+
+  public static void testInvalidateOverlay() {
+      mOverlayView.invalidate();
+  }
+
 }
index 1862fd5..f3cf0f7 100755 (executable)
@@ -33,8 +33,11 @@ import com.android.mediaframeworktest.functional.MediaPresetReverbTest;
 import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
 import com.android.mediaframeworktest.functional.MediaVisualizerTest;
 /*import for VideoEditor Test cases*/
+import com.android.mediaframeworktest.functional.MediaItemThumbnailTest;
+import com.android.mediaframeworktest.functional.MediaPropertiesTest;
 import com.android.mediaframeworktest.functional.VideoEditorAPITest;
-
+import com.android.mediaframeworktest.functional.VideoEditorExportTest;
+import com.android.mediaframeworktest.functional.VideoEditorPreviewTest;
 import junit.framework.TestSuite;
 
 import android.test.InstrumentationTestRunner;
@@ -73,7 +76,11 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
         suite.addTestSuite(MediaVirtualizerTest.class);
         suite.addTestSuite(MediaVisualizerTest.class);
         /*Test for Video Editor*/
+        suite.addTestSuite(MediaItemThumbnailTest.class);
+        suite.addTestSuite(MediaPropertiesTest.class);
         suite.addTestSuite(VideoEditorAPITest.class);
+        suite.addTestSuite(VideoEditorExportTest.class);
+        suite.addTestSuite(VideoEditorPreviewTest.class);
         return suite;
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java
new file mode 100755 (executable)
index 0000000..895ca25
--- /dev/null
@@ -0,0 +1,954 @@
+/*
+ * Copyright (C) 2011 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.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.graphics.Bitmap;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.VideoEditor;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class MediaItemThumbnailTest extends
+    ActivityInstrumentationTestCase<MediaFrameworkTest> {
+    private final String TAG = "MediaItemThumbailTest";
+
+    private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+    private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+    private VideoEditor mVideoEditor;
+
+    private VideoEditorHelper mVideoEditorHelper;
+
+    public MediaItemThumbnailTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        // setup for each test case.
+        super.setUp();
+        mVideoEditorHelper = new VideoEditorHelper();
+        // Create a random String which will be used as project path, where all
+        // project related files will be stored.
+        final String projectPath = mVideoEditorHelper.
+            createRandomFile(PROJECT_LOCATION);
+        mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+        // Clean the directory created as project path
+        mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+        System.gc();
+        super.tearDown();
+    }
+
+    protected void validateThumbnail(Bitmap thumbNailBmp, int outWidth,
+        int outHeight) throws Exception {
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Height", outHeight, thumbNailBmp.getHeight());
+        assertEquals("Thumbnail Width", outWidth, thumbNailBmp.getWidth());
+        thumbNailBmp.recycle();
+    }
+
+    // -----------------------------------------------------------------
+    // THUMBNAIL
+    // -----------------------------------------------------------------
+    /**
+     * To test thumbnail / frame extraction on H.263 QCIF.
+     */
+    // TODO : TC_TN_001
+    @LargeTest
+    public void testThumbnailForH263QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = mediaVideoItem.getHeight();
+
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on MPEG4 VGA .
+     */
+    // TODO : TC_TN_002
+    @LargeTest
+    public void testThumbnailForMPEG4VGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp";
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = mediaVideoItem.getHeight();
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on MPEG4 NTSC.
+     */
+    // TODO : TC_TN_003
+    @LargeTest
+    public void testThumbnailForMPEG4NTSC() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth() / 2;
+        final int outHeight = mediaVideoItem.getHeight() / 2;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on MPEG4 WVGA.
+     */
+    // TODO : TC_TN_004
+    @LargeTest
+    public void testThumbnailForMPEG4WVGA() throws Exception {
+
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth() * 2;
+        final int outHeight = mediaVideoItem.getHeight();
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on MPEG4 QCIF.
+     */
+    // TODO : TC_TN_005
+    @LargeTest
+    public void testThumbnailForMPEG4QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth();
+        final int outHeight = mediaVideoItem.getHeight() * 2;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on H264 QCIF.
+     */
+    // TODO : TC_TN_006
+    @LargeTest
+    public void testThumbnailForH264QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp";
+
+        final int atTime = 0;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth() * 2;
+        final int outHeight = mediaVideoItem.getHeight() * 2;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on H264 VGA.
+     */
+    // TODO : TC_TN_007
+    @LargeTest
+    public void testThumbnailForH264VGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+        final int outWidth = 32;
+        final int outHeight = 32;
+        final int atTime = 0;
+
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+    /**
+     * To test thumbnail / frame extraction on H264 WVGA.
+     */
+    // TODO : TC_TN_008
+    @LargeTest
+    public void testThumbnailForH264WVGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int outWidth = 64;
+        final int outHeight = 64;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final long atTime = mediaVideoItem.getDuration() / 2;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on H264 854x480.
+     */
+    // TODO : TC_TN_009
+    @LargeTest
+    public void testThumbnailForH264854_480() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4";
+        final int outWidth = 128;
+        final int outHeight = 128;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        MediaVideoItem mediaVideoItem = null;
+        mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final long atTime = mediaVideoItem.getDuration() - 1000;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on H264 960x720.
+     */
+    // TODO : TC_TN_010
+    @LargeTest
+    public void testThumbnailForH264HD960() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4";
+        final int outWidth = 75;
+        final int outHeight = 75;
+
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final long atTime = mediaVideoItem.getDuration() - 1000;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on H264 1080x720 .
+     */
+    // TODO : TC_TN_011
+    @LargeTest
+    public void testThumbnailForH264HD1080() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth() / 2;
+        final int outHeight = mediaVideoItem.getHeight() / 2;
+        final long atTime = mediaVideoItem.getDuration() / 4;
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * Check the thumbnail / frame extraction precision at 0,100 and 200 ms
+     */
+    // TODO : TC_TN_012
+    @LargeTest
+    public void testThumbnailForH264VGADifferentDuration() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        final int atTime = 0;
+        final int atTime1 = 100;
+        final int atTime2 = 200;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = mediaVideoItem.getWidth();
+        final int outHeight = mediaVideoItem.getHeight();
+
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+        // get Thumbnail @ 100ms
+        final Bitmap thumbNailBmpAt100 =
+            mediaVideoItem.getThumbnail(outWidth, outHeight, atTime1);
+        validateThumbnail(thumbNailBmpAt100, outWidth, outHeight);
+
+        // get Thumbnail @ 200ms
+        final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail(
+            outWidth, outHeight, atTime2);
+        validateThumbnail(thumbNailBmpAt200, outWidth, outHeight);
+    }
+
+    /**
+     *Check the thumbnail / frame extraction precision at
+     * FileDuration,FileDuration/2 + 100 andFileDuration/2 + 200 ms
+     */
+    // TODO : TC_TN_013
+    @LargeTest
+    public void testThumbnailForMP4VGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        final int outWidth = mediaVideoItem.getWidth();
+        final int outHeight = mediaVideoItem.getHeight();
+        final long atTime = mediaVideoItem.getDuration() / 2;
+        final long atTime1 = atTime + 100;
+        final long atTime2 = atTime + 200;
+
+        // get Thumbnail @ duration/2
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+        // get Thumbnail @ duration/2 + 100ms
+        final Bitmap thumbNailBmpAt100 = mediaVideoItem.getThumbnail(
+            outWidth, outHeight, atTime1);
+        validateThumbnail(thumbNailBmpAt100, outWidth, outHeight);
+
+        // get Thumbnail @ duration/2 + 200ms
+        final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail(
+            outWidth, outHeight, atTime2);
+        validateThumbnail(thumbNailBmpAt200, outWidth, outHeight);
+    }
+
+    /**
+     * Check the thumbnail / frame extraction on JPEG file
+     */
+    // TODO : TC_TN_014
+    @LargeTest
+    public void testThumbnailForImage() throws Exception {
+        final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final int mediaDuration = 1000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        int outWidth = 0;
+        int outHeight = 0;
+
+        final MediaImageItem mii = mVideoEditorHelper.createMediaItem(
+            mVideoEditor, "m1", imageItemFilename, mediaDuration, renderingMode);
+        assertNotNull("Media Image Item is Null",  mii);
+        outWidth =  mii.getWidth() / 2;
+        outHeight =  mii.getHeight() / 2;
+
+        final Bitmap thumbNailBmp = mii.getThumbnail(outWidth,
+            outHeight, mediaDuration);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+    /**
+     *To test ThumbnailList for H263 QCIF
+     */
+    // TODO : TC_TN_015
+    @LargeTest
+    public void testThumbnailListH263QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        final int startTime = 0;
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = mediaVideoItem.getWidth() / 4;
+        final int outHeight = mediaVideoItem.getHeight() / 4;
+        final long endTime = mediaVideoItem.getDuration() / 2;
+
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+            outWidth, outHeight, startTime, endTime, tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for MPEG4 QCIF
+     */
+    // TODO : TC_TN_016
+    @LargeTest
+    public void testThumbnailListMPEG4QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = mediaVideoItem.getWidth() / 2;
+        final int outHeight = mediaVideoItem.getHeight() / 2;
+        final long startTime = mediaVideoItem.getDuration() / 2;
+        final long endTime = mediaVideoItem.getDuration();
+
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+            outWidth, outHeight, startTime, endTime, tnCount);
+
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for H264 VGA
+     */
+    // TODO : TC_TN_017
+    @LargeTest
+    public void testThumbnailListH264VGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = mediaVideoItem.getWidth() / 2;
+        final int outHeight = mediaVideoItem.getHeight() / 2;
+        final long startTime = mediaVideoItem.getDuration() / 3;
+        final long endTime = mediaVideoItem.getDuration() / 2;
+
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+            outWidth, outHeight, startTime, endTime, tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for H264 WVGA
+     */
+    // TODO : TC_TN_018
+    @LargeTest
+    public void testThumbnailListH264WVGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = mediaVideoItem.getWidth() / 2;
+        final int outHeight = mediaVideoItem.getHeight() / 2;
+        final long startTime = mediaVideoItem.getDuration() / 3;
+        final long endTime = mediaVideoItem.getDuration() / 2;
+
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+            outWidth, outHeight, startTime, endTime, tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for H264 VGA ,Time exceeding file duration
+     */
+    // TODO : TC_TN_019
+    @LargeTest
+    public void testThumbnailH264VGAExceedingFileDuration() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        boolean flagForException = false;
+        int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        try {
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = mediaVideoItem.getWidth() / 2;
+            final int outHeight = mediaVideoItem.getHeight() / 2;
+            final long atTime = mediaVideoItem.getDuration() + 2000;
+            mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Exception in Extracting thumbanil with Invalid Time",
+            flagForException);
+    }
+
+    /**
+     *To test ThumbnailList for VGA Image
+     */
+    // TODO : TC_TN_020
+    @LargeTest
+    public void testThumbnailListVGAImage() throws Exception {
+        final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final int imageItemDuration = 10000;
+        final int startTime = 0;
+        final int endTime = 0;
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaImageItem mediaImageItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                imageItemFilename, imageItemDuration, renderingMode);
+        final int outWidth = mediaImageItem.getWidth() / 2;
+        final int outHeight = mediaImageItem.getHeight() / 2;
+
+        final Bitmap thumbNailBmp[] = mediaImageItem.getThumbnailList
+            (outWidth, outHeight, startTime, endTime, tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for Invalid file path
+     */
+    // TODO : TC_TN_021
+    @LargeTest
+    public void testThumbnailForInvalidFilePath() throws Exception {
+        final String imageItemFileName = INPUT_FILE_PATH + "/sdcard/abc.jpg";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try{
+        final MediaImageItem mii = new MediaImageItem(mVideoEditor, "m1",
+            imageItemFileName, 3000, renderingMode);
+        }catch (IllegalArgumentException e){
+            flagForException = true;
+        }
+        assertTrue(" Invalid File Path", flagForException);
+    }
+
+    /**
+     * To test thumbnail / frame extraction with setBoundaries
+     */
+    // TODO : TC_TN_022
+    @LargeTest
+    public void testThumbnailForMPEG4WVGAWithSetBoundaries() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+        final int atTime = 10000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        mediaVideoItem.setExtractBoundaries(1000,
+            (mediaVideoItem.getDuration() - 21000));
+
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = (mediaVideoItem.getHeight() / 2);
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     *To test ThumbnailList for H264 WVGA with setExtractboundaries
+     */
+    // TODO : TC_TN_023
+    @LargeTest
+    public void testThumbnailListForH264WVGAWithSetBoundaries() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_1_17.mp4";
+        final int thumbNailStartTime = 10000;
+        final int thumbNailEndTime = 12000;
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = (mediaVideoItem.getHeight() / 2);
+
+        mediaVideoItem.setExtractBoundaries(10000, 12000);
+
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList
+            (outWidth, outHeight, thumbNailStartTime, thumbNailEndTime,
+             tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertTrue("Thumbnail Size", (thumbNailBmp.length > 0) ? true : false);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList for H264 WVGA with count > frame available
+     */
+    // TODO : TC_TN_024
+    @LargeTest
+    public void testThumbnailListForH264WVGAWithCount() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int tnCount = 100;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = (mediaVideoItem.getHeight() / 2);
+        final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+        final long thumbNailEndTime = thumbNailStartTime + 4000;
+        Bitmap thumbNailBmp[] = null;
+        boolean flagForException = false;
+        try{
+            thumbNailBmp = mediaVideoItem.getThumbnailList(outWidth, outHeight,
+                thumbNailStartTime, thumbNailEndTime, tnCount);
+        }catch (Exception e){
+            assertTrue("Unable to get Thumbnail list", flagForException);
+        }
+        if (thumbNailBmp.length <= tnCount) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail count more than asked", flagForException);
+    }
+
+    /**
+     *To test ThumbnailList for H264 WVGA with startTime > End Time
+     */
+    // TODO : TC_TN_025
+    @LargeTest
+    public void testThumbnailListH264WVGAWithStartGreaterEnd() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final int tnCount = 10;
+        boolean flagForException = false;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = (mediaVideoItem.getHeight() / 2);
+        final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+        final long thumbNailEndTime = thumbNailStartTime - 1000;
+        try{
+            mediaVideoItem.getThumbnailList(outWidth, outHeight,
+                thumbNailStartTime, thumbNailEndTime, tnCount);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail Extraction where start time > end time",
+            flagForException);
+    }
+
+    /**
+     *To test ThumbnailList TC_TN_026 for H264 WVGA with startTime = End Time
+     */
+    // TODO : TC_TN_026
+    @LargeTest
+    public void testThumbnailListH264WVGAWithStartEqualEnd() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int tnCount = 1;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = (mediaVideoItem.getWidth() / 2);
+        final int outHeight = (mediaVideoItem.getHeight() / 2);
+        final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+        final long thumbNailEndTime = thumbNailStartTime;
+        final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(outWidth,
+            outHeight, thumbNailStartTime, thumbNailEndTime, tnCount);
+        assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+        assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+        for (int i = 0; i < thumbNailBmp.length; i++) {
+            validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+            thumbNailBmp[i] = null;
+        }
+    }
+
+    /**
+     *To test ThumbnailList TC_TN_027 for file where video duration is less
+     * than file duration.
+     */
+    // TODO : TC_TN_027
+    @LargeTest
+    public void testThumbnailForVideoDurationLessFileDuration() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = (mediaVideoItem.getWidth() / 2);
+            final int outHeight = (mediaVideoItem.getHeight() / 2);
+            final long atTime = mediaVideoItem.getDuration() - 2000;
+            final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+                outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+    }
+
+    /**
+     *To test ThumbnailList TC_TN_028 for file which has video part corrupted
+     */
+    // TODO : TC_TN_028
+    @LargeTest
+    public void testThumbnailWithCorruptedVideoPart() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+
+        try {
+            final MediaVideoItem mediaVideoItem =
+                 mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = mediaVideoItem.getWidth();
+            final int outHeight = mediaVideoItem.getHeight() * 2;
+            final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail
+                (outWidth, outHeight, mediaVideoItem.getDuration()/2);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Corrupted File cannot be read", flagForException);
+    }
+
+    /**
+     * Check the thumbnail / frame list extraction for Height as Negative Value
+     */
+    // TODO : TC_TN_029
+    @LargeTest
+    public void testThumbnailWithNegativeHeight() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try {
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = (mediaVideoItem.getWidth() / 2);
+            final int outHeight = -1;
+            final long thumbNailStartTime =
+                mediaVideoItem.getBoundaryBeginTime()/2;
+            final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime();
+            mediaVideoItem.getThumbnailList(outWidth, outHeight,
+                thumbNailStartTime, thumbNailEndTime, tnCount);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail List with negative Height", flagForException);
+    }
+
+    /**
+     * Check the thumbnail for Height as Zero
+     */
+    // TODO : TC_TN_030
+    @LargeTest
+    public void testThumbnailWithHeightAsZero() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int atTime = 100;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try {
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = (mediaVideoItem.getWidth() / 2);
+            final int outHeight = -1;
+            mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail List with Zero Height", flagForException);
+    }
+
+    /**
+     * Check the thumbnail for Height = 10
+     */
+    // TODO : TC_TN_031
+    @LargeTest
+    public void testThumbnailWithHeight() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int atTime = 1000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = (mediaVideoItem.getWidth() / 2);
+            final int outHeight = 10;
+            final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+                outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * Check the thumbnail / frame list extraction for Width as Negative Value
+     */
+    // TODO : TC_TN_032
+    @LargeTest
+    public void testThumbnailWithNegativeWidth() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int tnCount = 10;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try {
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = -1;
+            final int outHeight = mediaVideoItem.getHeight();
+            final long thumbNailStartTime =
+                mediaVideoItem.getBoundaryBeginTime()/2;
+            final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime();
+            mediaVideoItem.getThumbnailList(outWidth, outHeight, thumbNailStartTime,
+                thumbNailEndTime, tnCount);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail List with negative Height", flagForException);
+    }
+
+    /**
+     * Check the thumbnail / frame list extraction for Width zero
+     */
+    // TODO : TC_TN_033
+    @LargeTest
+    public void testThumbnailWithWidthAsZero() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int atTime = 1000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try {
+            final MediaVideoItem mediaVideoItem =
+                mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                    videoItemFilename, renderingMode);
+            final int outWidth = 0;
+            final int outHeight = mediaVideoItem.getHeight() / 2;
+            mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail List with Zero Width", flagForException);
+    }
+
+    /**
+     * Check the thumbnail for Width = 10
+     */
+    // TODO : TC_TN_034
+    @LargeTest
+    public void testThumbnailWithWidth() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        final int atTime = 1000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth = 10;
+        final int outHeight = mediaVideoItem.getHeight();
+        final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+            outHeight, atTime);
+        validateThumbnail(thumbNailBmp, outWidth, outHeight);
+    }
+
+    /**
+     * To test thumbnail / frame extraction on MPEG4 (time beyond file duration).
+     */
+    // TODO : TC_TN_035
+    @LargeTest
+    public void testThumbnailMPEG4withMorethanFileDuration() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+        boolean flagForException = false;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename, renderingMode);
+        final int outWidth =  mediaVideoItem.getWidth()/2;
+        final int outHeight =  mediaVideoItem.getHeight()/2;
+        final long atTime = mediaVideoItem.getDuration() + 100;
+        try{
+            final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+            outHeight, atTime);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Thumbnail duration is more than file duration",
+            flagForException);
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
new file mode 100755 (executable)
index 0000000..3efa5b2
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2011 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.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.VideoEditor;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class MediaPropertiesTest extends
+    ActivityInstrumentationTestCase<MediaFrameworkTest> {
+    private final String TAG = "MediaPropertiesTest";
+
+    private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+    private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+    private VideoEditor mVideoEditor;
+
+    private VideoEditorHelper mVideoEditorHelper;
+
+    public MediaPropertiesTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        // setup for each test case.
+        super.setUp();
+        mVideoEditorHelper = new VideoEditorHelper();
+        // Create a random String which will be used as project path,
+        // where all project related files will be stored.
+        final String projectPath = mVideoEditorHelper.
+            createRandomFile(PROJECT_LOCATION);
+        mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+        // Clean the directory created as project path
+        mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+        System.gc();
+        super.tearDown();
+    }
+
+    protected void validateVideoProperties(int aspectRatio, int fileType,
+        int videoCodecType, int duration, int videoBitrate, int fps,
+        int videoProfile, int width, int height, int audioCodecType,
+        int audioSamplingFrequency, int audioChannel, int audioBitrate,
+        MediaVideoItem mvi) throws Exception {
+        assertEquals("Aspect Ratio Mismatch", aspectRatio, mvi.getAspectRatio());
+        assertEquals("File Type Mismatch", fileType, mvi.getFileType());
+        assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType());
+
+        assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange (
+            duration, mvi.getDuration(), 10));
+        assertEquals("Video Profile " + mvi.getVideoProfile(), videoProfile,
+            mvi.getVideoProfile());
+        assertEquals("Video height " + mvi.getHeight(), height, mvi.getHeight());
+        assertEquals("Video width " + mvi.getWidth(), width, mvi.getWidth());
+        /** Check FPS with 10% range */
+        assertTrue("fps Mismatch" + mvi.getFps(),
+            mVideoEditorHelper.checkRange(fps, mvi.getFps(), 10));
+
+        assertEquals("AudioType Mismatch ", audioCodecType, mvi.getAudioType());
+        assertEquals("Audio Sampling " + mvi.getAudioSamplingFrequency(),
+            audioSamplingFrequency, mvi.getAudioSamplingFrequency());
+        assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel,
+            mvi.getAudioChannels());
+    }
+
+    protected void validateAudioProperties(int audioCodecType, int duration,
+        int audioSamplingFrequency, int audioChannel, int audioBitrate,
+        AudioTrack aT) throws Exception {
+        assertEquals("AudioType Mismatch ", audioCodecType, aT.getAudioType());
+        assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange (
+            duration, aT.getDuration(), 10));
+        assertEquals("Audio Sampling " + aT.getAudioSamplingFrequency(),
+            audioSamplingFrequency, aT.getAudioSamplingFrequency());
+        assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel,
+            aT.getAudioChannels());
+    }
+
+    protected void validateImageProperties(int aspectRatio, int fileType,
+        int width, int height, MediaImageItem mii)
+        throws Exception {
+        assertEquals("Aspect Ratio Mismatch", aspectRatio, mii.getAspectRatio());
+        assertEquals("File Type Mismatch", fileType, mii.getFileType());
+        assertEquals("Image height " + mii.getHeight(), height, mii.getHeight());
+        assertEquals("Image width " + mii.getWidth(), width, mii.getWidth());
+    }
+
+
+    /**
+     *To test Media Properties for file MPEG4 854 x 480
+     */
+    // TODO : Remove TC_MP_001
+    @LargeTest
+    public void testPropertiesMPEG4854_480() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+        final int duration = 26933;
+        final int videoBitrate = 319000;
+        final int audioBitrate = 48000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 16000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+        final int width = 854;
+        final int height = MediaProperties.HEIGHT_480;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename,
+            MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+
+    /**
+     *To test Media Properties for file MPEG4 WVGA
+     */
+    // TODO : Remove TC_MP_002
+    @LargeTest
+    public void testPropertiesMPEGWVGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+        final int duration = 26933;
+        final int videoBitrate = 384000;
+        final int audioBitrate = 12800;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+        final int audioSamplingFrequency = 8000;
+        final int audioChannel = 1;
+        final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+        final int width = 800;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test media properties for MPEG4 720x480 (NTSC) + AAC file.
+     */
+    // TODO : Remove TC_MP_003
+    @LargeTest
+    public void testPropertiesMPEGNTSC() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+        final int duration = 26866;
+        final int videoBitrate = 403000;
+        final int audioBitrate = 160000;
+        final int fps = 30;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 48000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+        final int width = 720;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file MPEG4 VGA
+     */
+    // TODO : Remove TC_MP_004
+    @LargeTest
+    public void testPropertiesMPEGVGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+        final int duration = 26933;
+        final int videoBitrate = 533000;
+        final int audioBitrate = 128000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 48000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+        final int width = 640;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file MPEG4 QCIF
+     */
+    // TODO : Remove TC_MP_005
+    @LargeTest
+    public void testPropertiesMPEGQCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9;
+        final int fileType = MediaProperties.FILE_3GP;
+        final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+        final int duration = 27000;
+        final int videoBitrate = 384000;
+        final int audioBitrate = 12200;
+        final int fps = 12;
+        final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+        final int audioSamplingFrequency = 8000;
+        final int audioChannel = 1;
+        final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+        final int width = 176;
+        final int height = MediaProperties.HEIGHT_144;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To To test media properties for H263 176x144 (QCIF) + AAC (mono) file.
+     */
+    // TODO : Remove TC_MP_006
+    @LargeTest
+    public void testPropertiesH263QCIF() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9;
+        final int fileType = MediaProperties.FILE_3GP;
+        final int videoCodecType = MediaProperties.VCODEC_H263;
+        final int duration = 26933;
+        final int videoBitrate = 384000;
+        final int audioBitrate = 64000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 16000;
+        final int audioChannel = 1;
+        final int videoProfile = MediaProperties.H263_PROFILE_0_LEVEL_10;
+        final int width = 176;
+        final int height = MediaProperties.HEIGHT_144;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file H264 VGA
+     */
+    // TODO : Remove TC_MP_007
+    @LargeTest
+    public void testPropertiesH264VGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        final int fileType = MediaProperties.FILE_3GP;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 77600;
+        final int videoBitrate = 745000;
+        final int audioBitrate = 64000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 48000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 640;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file H264 NTSC
+     */
+    // TODO : Remove TC_MP_008
+    @LargeTest
+    public void testPropertiesH264NTSC() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 26880;
+        final int videoBitrate = 244000;
+        final int audioBitrate = 12200;
+        final int fps = 25;
+        final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+        final int audioSamplingFrequency = 8000;
+        final int audioChannel = 1;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 720;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test media properties for H264 800x480 (WVGA) + AAC file.
+     */
+    // TODO : Remove TC_MP_009
+    @LargeTest
+    public void testPropertiesH264WVGA() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+              "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 77466;
+        final int videoBitrate = 528000;
+        final int audioBitrate = 38000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 24000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 800;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file H264 HD1280
+     */
+    // TODO : Remove TC_MP_010
+    @LargeTest
+    public void testPropertiesH264HD1280() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 77600;
+        final int videoBitrate = 606000;
+        final int audioBitrate = 48000;
+        final int fps = 15;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 16000;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 1280;
+        final int height = MediaProperties.HEIGHT_720;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test media properties for H264 1080x720 + AAC file
+     */
+    // TODO : Remove TC_MP_011
+    @LargeTest
+    public void testPropertiesH264HD1080WithAudio() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 77500;
+        final int videoBitrate = 1190000;
+        final int audioBitrate = 64000;
+        final int fps = 10;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 44100;
+        final int audioChannel = 2;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 1080;
+        final int height = MediaProperties.HEIGHT_720;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for file WMV - Unsupported type
+     */
+    // TODO : Remove TC_MP_012
+    @LargeTest
+    public void testPropertiesWMVFile() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "WMV_V7_640x480_15fps_512Kbps_wma_V9_44khz_48Kbps_s_1_30.wmv";
+        boolean flagForException = false;
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+                MediaItem.RENDERING_MODE_BLACK_BORDER);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for a WMV File -- Unsupported file type",
+            flagForException);
+    }
+
+    /**
+     *To test media properties for H.264 Main/Advanced profile. (unsupported profile input)
+     */
+    // TODO : Remove TC_MP_013
+    @LargeTest
+    public void testPropertiesH264MainLineProfile() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH
+            + "H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        //final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int videoCodecType = MediaProperties.VCODEC_H264MP;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+
+        try {
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+            assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType());
+        }catch (IllegalArgumentException e){
+            flagForException = true;
+        }
+            assertTrue("Unsupported Main Profile", flagForException);
+    }
+
+    /**
+     *To test Media Properties for non existing file.
+     */
+    // TODO : Remove TC_MP_014
+    @LargeTest
+    public void testPropertiesForNonExsitingFile() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH + "abc.3gp";
+        boolean flagForException = false;
+
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+                MediaItem.RENDERING_MODE_BLACK_BORDER);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for non exsisting file", flagForException);
+     }
+
+    /**
+     *To test Media Properties for file H264 HD1080
+     */
+    // TODO : Remove TC_MP_015
+    @LargeTest
+    public void testPropertiesH264HD1080WithoutAudio() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+        final int fileType = MediaProperties.FILE_MP4;
+        final int videoCodecType = MediaProperties.VCODEC_H264BP;
+        final int duration = 77366;
+        final int videoBitrate = 859000;
+        final int audioBitrate = 0;
+        final int fps = 30;
+        final int audioCodecType = -1;
+        final int audioSamplingFrequency = 0;
+        final int audioChannel = 0;
+        final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+        final int width = 1080;
+        final int height = MediaProperties.HEIGHT_720;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+        validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+            videoBitrate, fps, videoProfile, width, height, audioCodecType,
+            audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+    }
+
+    /**
+     *To test Media Properties for Image file of JPEG Type
+     */
+    // TODO : Remove TC_MP_016
+    @LargeTest
+    public void testPropertiesVGAImage() throws Exception {
+        final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final int imageItemDuration = 10000;
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        final int fileType = MediaProperties.FILE_JPEG;
+        final int width = 640;
+        final int height = MediaProperties.HEIGHT_480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+        final MediaImageItem mii = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", imageItemFilename, imageItemDuration,
+            renderingMode);
+        validateImageProperties(aspectRatio, fileType, width, height, mii);
+    }
+
+    /**
+     *To test Media Properties for Image file of PNG Type
+     */
+    // TODO : Remove TC_MP_017
+    @LargeTest
+    public void testPropertiesPNG() throws Exception {
+        final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.png";
+        final int imageItemDuration = 10000;
+        final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        final int fileType = MediaProperties.FILE_PNG;
+        final int width = 640;
+        final int height = 480;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final MediaImageItem mii = mVideoEditorHelper.createMediaItem
+            (mVideoEditor, "m1", imageItemFilename, imageItemDuration,
+            renderingMode);
+        validateImageProperties(aspectRatio, fileType, width, height, mii);
+    }
+
+    /**
+     *To test Media Properties for file GIF - Unsupported type
+     */
+    // TODO : Remove TC_MP_018
+    @LargeTest
+    public void testPropertiesGIFFile() throws Exception {
+
+        final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.gif";
+        final int imageItemDuration = 10000;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        try {
+            new MediaImageItem(mVideoEditor, "m1", imageItemFilename,
+                imageItemDuration, renderingMode);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for a GIF File -- Unsupported file type",
+            flagForException);
+    }
+
+    /**
+     *To test Media Properties for file Text file named as 3GP
+     */
+    // TODO : Remove TC_MP_019
+    @LargeTest
+    public void testPropertiesofDirtyFile() throws Exception {
+
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "Text_FileRenamedTo3gp.3gp";
+        boolean flagForException = false;
+
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+                MediaItem.RENDERING_MODE_BLACK_BORDER);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for a Dirty  File ",
+            flagForException);
+    }
+
+    /**
+     *To test Media Properties for file name as NULL
+     */
+    // TODO : Remove TC_MP_020
+    @LargeTest
+    public void testPropertieNULLFile() throws Exception {
+        final String videoItemFilename = null;
+        boolean flagForException = false;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+                renderingMode);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for NULL  File ",
+            flagForException);
+    }
+
+    /**
+     *To test Media Properties for file which is of type MPEG2
+     */
+    // TODO : Remove TC_MP_021
+    @LargeTest
+    public void testPropertiesMPEG2File() throws Exception {
+        final String videoItemFilename = INPUT_FILE_PATH +
+            "MPEG2_640x480_30fps_192kbps_1_5.mp4";
+        boolean flagForException = false;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+                renderingMode);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Media Properties for a MPEG2 File --Unsupported file type",
+            flagForException);
+    }
+
+    /**
+     *To test Media Properties TC_MP_023 for file without Video only Audio
+     */
+    // TODO : Remove TC_MP_023
+    @LargeTest
+    public void testProperties3GPWithoutVideoMediaItem() throws Exception {
+        final String audioFilename = INPUT_FILE_PATH +
+            "AACLC_48KHz_256Kbps_s_1_17.3gp";
+        boolean flagForException = false;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        try {
+            new MediaVideoItem(mVideoEditor, "m1", audioFilename,
+                renderingMode);
+        } catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Exception in Creaing Media Video item object without video",
+            flagForException);
+    }
+
+    /**
+     *To test media properties for Audio Track file. (No Video, AAC Audio)
+     */
+    // TODO : Remove TC_MP_024
+    @LargeTest
+    public void testProperties3GPWithoutVideoAudioTrack() throws Exception {
+
+        final String audioFilename = INPUT_FILE_PATH +
+            "AACLC_44.1kHz_256kbps_s_1_17.mp4";
+        final int duration = 77554;
+        final int audioBitrate = 384000;
+        final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+        final int audioSamplingFrequency = 44100;
+        final int audioChannel = 2;
+
+        final AudioTrack audioTrack = mVideoEditorHelper.createAudio
+            (mVideoEditor, "a1", audioFilename);
+
+        validateAudioProperties(audioCodecType, duration, audioSamplingFrequency,
+            audioChannel, audioBitrate, audioTrack);
+    }
+
+        /**
+     *To test media properties for Audio Track file. MP3 file
+     */
+    // TODO : Remove TC_MP_025
+    @LargeTest
+    public void testPropertiesMP3AudioTrack() throws Exception {
+
+        final String audioFilename = INPUT_FILE_PATH +
+            "MP3_48KHz_128kbps_s_1_17.mp3";
+        final int duration = 77640;
+        final int audioBitrate = 128000;
+        final int audioCodecType = MediaProperties.ACODEC_MP3;
+        final int audioSamplingFrequency = 48000;
+        final int audioChannel = 2;
+
+        final AudioTrack audioTrack = mVideoEditorHelper.createAudio
+            (mVideoEditor, "a1", audioFilename);
+
+        validateAudioProperties(audioCodecType, duration, audioSamplingFrequency,
+            audioChannel, audioBitrate, audioTrack);
+    }
+}
index 6a87656..0dadaa5 100644 (file)
@@ -89,7 +89,6 @@ public class VideoEditorAPITest extends
      * To Test Creation of Media Video Item.
      */
     // TODO : remove TC_API_001
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItem() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -132,7 +131,6 @@ public class VideoEditorAPITest extends
      * the Begin and End Time.
      */
     // TODO : remove TC_API_002
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemExtractBoundaries() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -202,7 +200,6 @@ public class VideoEditorAPITest extends
      * To test creation of Media Video Item with Set and Get rendering Mode
      */
     // TODO : remove TC_API_003
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemRenderingModes() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -247,7 +244,6 @@ public class VideoEditorAPITest extends
      * To Test the Media Video API : Set Audio Volume, Get Audio Volume and Mute
      */
     // TODO : remove TC_API_005
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemAudioFeatures() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -306,7 +302,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_006
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemGetWaveformData() throws Exception {
 
@@ -349,7 +344,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_007
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemEffect() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -391,7 +385,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_008
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemTransitions() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -439,7 +432,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_009
-    @SuppressWarnings("unused")
     @LargeTest
     public void testMediaVideoItemOverlays() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -1404,8 +1396,6 @@ public class VideoEditorAPITest extends
         final Rect endRect1 = new Rect(10, 10, mediaImageItem.getWidth() / 4,
             mediaImageItem.getHeight() / 4);
 
-        //kbEffectOnMediaItem.setStartRect(startRect1);
-        //kbEffectOnMediaItem.setEndRect(endRect1);
         /* Added newly to take care of removal set APIs */
         kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_changed",
             startRect1, endRect1, 500, 3000);
@@ -1417,7 +1407,6 @@ public class VideoEditorAPITest extends
 
         final Rect zeroRect = new Rect(0, 0, 0, 0);
         try {
-            //kbEffectOnMediaItem.setStartRect(zeroRect);
             kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroStart",
                 zeroRect, endRect, 500, 3000);
 
@@ -1428,7 +1417,6 @@ public class VideoEditorAPITest extends
 
         flagForException = false;
         try {
-            //kbEffectOnMediaItem.setEndRect(zeroRect);
             kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroEnd",
                 startRect, zeroRect, 500, 3000);
         } catch (IllegalArgumentException e) {
@@ -1443,7 +1431,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_037
-    @SuppressWarnings("unused")
     @LargeTest
     public void testTransitionFadeBlack() throws Exception {
 
@@ -1592,7 +1579,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_038
-    @SuppressWarnings("unused")
     @LargeTest
     public void testTransitionCrossFade() throws Exception {
 
@@ -1744,7 +1730,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_039
-    @SuppressWarnings("unused")
     @LargeTest
     public void testTransitionSliding() throws Exception {
         final String videoItemFilename1 = INPUT_FILE_PATH +
@@ -1935,7 +1920,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_040
-    @SuppressWarnings("unused")
     @LargeTest
     public void testTransitionAlpha() throws Exception {
 
@@ -2115,7 +2099,6 @@ public class VideoEditorAPITest extends
      */
 
     // TODO : remove TC_API_041
-    @SuppressWarnings("unused")
     @LargeTest
     public void testFrameOverlayVideoItem() throws Exception {
         final String videoItemFilename1 = INPUT_FILE_PATH +
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java
new file mode 100755 (executable)
index 0000000..37b1f54
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2011 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.mediaframeworktest.functional;
+
+import java.io.File;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.EffectColor;
+import android.media.videoeditor.EffectKenBurns;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.OverlayFrame;
+import android.media.videoeditor.Transition;
+import android.media.videoeditor.TransitionAlpha;
+import android.media.videoeditor.TransitionCrossfade;
+import android.media.videoeditor.TransitionFadeBlack;
+import android.media.videoeditor.TransitionSliding;
+import android.media.videoeditor.VideoEditor;
+import android.media.videoeditor.VideoEditor.ExportProgressListener;
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+
+
+import android.util.Log;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class VideoEditorExportTest extends
+    ActivityInstrumentationTestCase<MediaFrameworkTest> {
+    private final String TAG = "TransitionTest";
+
+    private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+    private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+    private VideoEditor mVideoEditor;
+
+    private VideoEditorHelper mVideoEditorHelper;
+
+    // Declares the annotation for Preview Test Cases
+    public @interface TransitionTests {
+    }
+
+    public VideoEditorExportTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        // setup for each test case.
+        super.setUp();
+        mVideoEditorHelper = new VideoEditorHelper();
+        // Create a random String which will be used as project path, where all
+        // project related files will be stored.
+        final String projectPath =
+            mVideoEditorHelper.createRandomFile(PROJECT_LOCATION);
+        mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+        // Clean the directory created as project path
+        mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+        System.gc();
+        super.tearDown();
+    }
+
+    /**
+     * To Test export : Merge and Trim different types of Video and Image files
+     */
+    // TODO :remove TC_EXP_001
+    @LargeTest
+    public void testExportMergeTrim() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String videoItemFilename2 = INPUT_FILE_PATH
+            + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final String videoItemFilename3 = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+        final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+        final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/")
+            + ".3gp";
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(2000, 7000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaImageItem mediaImageItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem2);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem3.setExtractBoundaries(0, 2000);
+        mVideoEditor.addMediaItem(mediaVideoItem3);
+
+        final MediaVideoItem mediaVideoItem4 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+                videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000,
+            mediaVideoItem4.getDuration());
+        mVideoEditor.addMediaItem(mediaVideoItem4);
+
+        final MediaImageItem mediaImageItem5 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+                imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem5);
+
+        final MediaImageItem mediaImageItem6 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+                imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem6);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        final long storyBoardDuration = mediaVideoItem1.getTimelineDuration()
+            + mediaImageItem2.getDuration() + mediaVideoItem3.getTimelineDuration()
+            + mediaVideoItem4.getTimelineDuration() + mediaImageItem5.getDuration()
+            + mediaImageItem6.getDuration();
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     *To Test export : With Effect and Overlays on Different Media Items
+     */
+    // TODO :remove TC_EXP_002
+    @LargeTest
+    public void testExportEffectOverlay() throws Exception {
+          final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String videoItemFilename2 = INPUT_FILE_PATH
+              + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+        final String videoItemFilename3 = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+        final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+        final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+        final String overlayFile = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png";
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(2000, 7000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final EffectColor effectPink =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectPink",
+                0, 2000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+        mediaVideoItem1.addEffect(effectPink);
+
+        final EffectColor effectNegative =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectNegative",
+                3000, 4000, EffectColor.TYPE_NEGATIVE, 0);
+        mediaVideoItem1.addEffect(effectNegative);
+
+        final MediaImageItem mediaImageItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem2);
+
+        final EffectColor effectFifties =
+            mVideoEditorHelper.createEffectItem(mediaImageItem2, "effectFifties",
+                0, 3000, EffectColor.TYPE_FIFTIES, 0);
+        mediaImageItem2.addEffect(effectFifties);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem3);
+        mediaVideoItem3.setExtractBoundaries(0, 8000);
+
+        final Bitmap mBitmap =  mVideoEditorHelper.getBitmap(overlayFile,
+            640, 480);
+        final OverlayFrame overlayFrame =
+            mVideoEditorHelper.createOverlay(mediaVideoItem3, "overlay",
+                mBitmap, 2000, 5000);
+        mediaVideoItem3.addOverlay(overlayFrame);
+
+        final EffectColor effectGreen =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem3, "effectGreen",
+                0, 2000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+        mediaVideoItem3.addEffect(effectGreen);
+
+        final MediaVideoItem mediaVideoItem4 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+                videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000,
+            mediaVideoItem4.getDuration());
+        mVideoEditor.addMediaItem(mediaVideoItem4);
+
+        final EffectColor effectSepia =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem4, "effectSepia",
+                0, 2000, EffectColor.TYPE_SEPIA, 0);
+        mediaVideoItem4.addEffect(effectSepia);
+
+        final MediaImageItem mediaImageItem5 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+                imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem5);
+
+        final EffectColor effectGray =
+            mVideoEditorHelper.createEffectItem(mediaImageItem5, "effectGray",
+                0, 2000, EffectColor.TYPE_COLOR, EffectColor.GRAY);
+        mediaImageItem5.addEffect(effectGray);
+
+        final MediaImageItem mediaImageItem6 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+                imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem6);
+
+        final EffectColor effectGradient =
+            mVideoEditorHelper.createEffectItem(mediaImageItem6,
+                "effectGradient", 0, 2000, EffectColor.TYPE_GRADIENT,
+                EffectColor.PINK);
+        mediaImageItem6.addEffect(effectGradient);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        final long storyBoardDuration = mediaVideoItem1.getTimelineDuration()
+            + mediaImageItem2.getDuration()
+            + mediaVideoItem3.getTimelineDuration()
+            + mediaVideoItem4.getTimelineDuration()
+            + mediaImageItem5.getDuration()
+            + mediaImageItem6.getDuration();
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     * To test export : with Image with KenBurnEffect
+     */
+    // TODO : remove TC_EXP_003
+    @LargeTest
+    public void testExportEffectKenBurn() throws Exception {
+        final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final int imageItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+        final MediaImageItem mediaImageItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1",
+                imageItemFileName, 5000, imageItemRenderingMode);
+        mVideoEditor.addMediaItem(mediaImageItem);
+
+        final Rect startRect = new Rect((mediaImageItem.getHeight() / 3),
+            (mediaImageItem.getWidth() / 3), (mediaImageItem.getHeight() / 2),
+            (mediaImageItem.getWidth() / 2));
+
+        final Rect endRect = new Rect(0, 0, mediaImageItem.getWidth(),
+            mediaImageItem.getHeight());
+
+        final EffectKenBurns kbEffectOnMediaItem = new EffectKenBurns(
+            mediaImageItem, "KBOnM2", startRect, endRect, 500, 3000);
+        assertNotNull("EffectKenBurns", kbEffectOnMediaItem);
+        mediaImageItem.addEffect(kbEffectOnMediaItem);
+
+        assertEquals("KenBurn Start Rect", startRect,
+            kbEffectOnMediaItem.getStartRect());
+        assertEquals("KenBurn End Rect", endRect,
+            kbEffectOnMediaItem.getEndRect());
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, mediaImageItem.getDuration(),
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     * To Test Export : With Video and Image and An Audio BackGround Track
+     */
+    // TODO : remove TC_EXP_004
+    @LargeTest
+    public void testExportAudio() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+        final String audioTrackFilename = INPUT_FILE_PATH +
+            "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final MediaImageItem mediaImageItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFileName, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem);
+
+        final AudioTrack audioTrack = mVideoEditorHelper.createAudio(
+            mVideoEditor, "a1", audioTrackFilename);
+        audioTrack.setExtractBoundaries(2000, 5000);
+        mVideoEditor.addAudioTrack(audioTrack);
+
+        audioTrack.disableDucking();
+        audioTrack.enableLoop();
+        audioTrack.setVolume(75);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, (mediaVideoItem.getTimelineDuration() +
+            mediaImageItem.getDuration()),
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     *To Test export : With Transition on Different Media Items
+     */
+    // TODO :remove TC_EXP_005
+    @LargeTest
+    public void testExportTransition() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String videoItemFilename2 = INPUT_FILE_PATH
+            + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final String videoItemFilename3 = INPUT_FILE_PATH +
+            "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+
+        final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+        final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+        final String maskFilename = INPUT_FILE_PATH +
+            "TransitionSpiral_QVGA.jpg";
+
+        final MediaVideoItem mediaItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaItem1.setExtractBoundaries(2000, 7000);
+        mVideoEditor.addMediaItem(mediaItem1);
+
+        final TransitionAlpha transition1 =
+            mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1,
+                2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true);
+        mVideoEditor.addTransition(transition1);
+
+        final MediaImageItem mediaItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem2);
+
+        final MediaVideoItem mediaItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaItem3.setExtractBoundaries(0, 8000);
+        mVideoEditor.addMediaItem(mediaItem3);
+
+        final TransitionSliding transition2And3 =
+            mVideoEditorHelper.createTSliding("transition2", mediaItem2,
+                mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST,
+                TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN);
+        mVideoEditor.addTransition(transition2And3);
+
+        final MediaVideoItem mediaItem4 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+                videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem4);
+        mediaItem4.setExtractBoundaries(0, 8000);
+
+        final TransitionCrossfade transition3And4 =
+            mVideoEditorHelper.createTCrossFade("transition3", mediaItem3,
+                mediaItem4, 3500, Transition.BEHAVIOR_MIDDLE_SLOW);
+        mVideoEditor.addTransition(transition3And4);
+
+        final MediaImageItem mediaItem5 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+                imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem5);
+
+        final TransitionFadeBlack transition4And5 =
+            mVideoEditorHelper.createTFadeBlack("transition4", mediaItem4,
+                mediaItem5, 3500, Transition.BEHAVIOR_SPEED_DOWN);
+        mVideoEditor.addTransition(transition4And5);
+
+        final MediaImageItem mediaItem6 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+                imageItemFilename3, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem6);
+
+        final TransitionSliding transition5And6 =
+            mVideoEditorHelper.createTSliding("transition5", mediaItem5,
+                mediaItem6, 1000/*4000*/, Transition.BEHAVIOR_SPEED_UP,
+                TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN);
+        mVideoEditor.addTransition(transition5And6);
+
+        final TransitionSliding transition6 =
+            mVideoEditorHelper.createTSliding("transition6", mediaItem6, null,
+                1000 /*4000*/, Transition.BEHAVIOR_SPEED_UP,
+                TransitionSliding.DIRECTION_TOP_OUT_BOTTOM_IN);
+        mVideoEditor.addTransition(transition6);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        final long storyBoardDuration = mediaItem1.getTimelineDuration()
+            + mediaItem2.getTimelineDuration()
+            + mediaItem3.getTimelineDuration() - transition2And3.getDuration()
+            + mediaItem4.getTimelineDuration() - transition3And4.getDuration()
+            + mediaItem5.getTimelineDuration() - transition4And5.getDuration()
+            + mediaItem6.getTimelineDuration() - transition5And6.getDuration();
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     * To Test Export : Without any Media Items in the story Board
+     *
+     * @throws Exception
+     */
+    // TODO :remove TC_EXP_006
+    @LargeTest
+    public void testExportWithoutMediaItems() throws Exception {
+        boolean flagForException = false;
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (IllegalStateException e) {
+            flagForException = true;
+        }
+        assertTrue("Export without any MediaItems", flagForException);
+    }
+
+    /**
+     * To Test Export : With Media Items add and removed in the story Board
+     *
+     * @throws Exception
+     */
+    // TODO :remove TC_EXP_007
+    @LargeTest
+    public void testExportWithoutMediaItemsAddRemove() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH +
+            "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg";
+        boolean flagForException = false;
+
+        final MediaVideoItem mediaItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaItem1.setExtractBoundaries(0, 15000);
+        mVideoEditor.addMediaItem(mediaItem1);
+
+        final MediaImageItem mediaItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 15000,
+                MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem2);
+
+        final TransitionAlpha transition1 =
+            mVideoEditorHelper.createTAlpha("transition1", mediaItem1, mediaItem2,
+                3000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, false);
+        mVideoEditor.addTransition(transition1);
+
+        final EffectColor effectColor =
+            mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 12000,
+                3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+        mediaItem2.addEffect(effectColor);
+
+        mVideoEditor.removeMediaItem(mediaItem1.getId());
+        mVideoEditor.removeMediaItem(mediaItem2.getId());
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (IllegalStateException e) {
+            flagForException = true;
+        }
+        assertTrue("Export with MediaItem added and removed", flagForException);
+    }
+
+    /**
+     * To Test Export : With Video and Image : MMS use case
+     *
+     * @throws Exception
+     */
+    // TODO :remove TC_EXP_008
+    @LargeTest
+    public void testExportMMS() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String videoItemFilename2 = INPUT_FILE_PATH
+            + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+        final MediaVideoItem mediaItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaItem1.setExtractBoundaries(2000, 7000);
+        mVideoEditor.addMediaItem(mediaItem1);
+
+        final TransitionAlpha transition1 =
+            mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1,
+                2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true);
+        mVideoEditor.addTransition(transition1);
+
+        final MediaImageItem mediaItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem2);
+
+        final MediaVideoItem mediaItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaItem3.setExtractBoundaries(0, 8000);
+        mVideoEditor.addMediaItem(mediaItem3);
+
+        final TransitionSliding transition2And3 =
+            mVideoEditorHelper.createTSliding("transition2", mediaItem2,
+                mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST,
+                TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN);
+        mVideoEditor.addTransition(transition2And3);
+
+        final TransitionCrossfade transition3 =
+            mVideoEditorHelper.createTCrossFade("transition3", mediaItem3, null,
+                3500, Transition.BEHAVIOR_MIDDLE_SLOW);
+        mVideoEditor.addTransition(transition3);
+
+        final EffectColor effectColor =
+            mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 0,
+                3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+        mediaItem2.addEffect(effectColor);
+
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_11_9);
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        final long storyBoardDuration = mediaItem1.getTimelineDuration()
+            + mediaItem2.getTimelineDuration() + mediaItem3.getTimelineDuration()
+            - transition2And3.getDuration();
+
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_144, 0, storyBoardDuration,
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+         mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     * To Test Export :Media Item having duration of 1 Hour
+     *
+     * @throws Exception
+     */
+    @LargeTest
+    public void testExportDuration1Hour() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH +
+            "H264_BP_640x480_15fps_384kbps_60_0.mp4";
+        final String outFilename = mVideoEditorHelper.createRandomFile(
+            mVideoEditor.getPath() + "/") + ".3gp";
+
+        final MediaVideoItem mediaItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaItem1);
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        }catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0, mediaItem1.getDuration(),
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+
+    /**
+     * To Test Export : Storage location having very less space (Less than 100
+     * KB)
+     *
+     * @throws Exception
+     */
+    @LargeTest
+    public void testExportWithStorageFull() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+        boolean flagForException = false;
+
+        mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", videoItemFilename1,
+            MediaItem.RENDERING_MODE_BLACK_BORDER);
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            flagForException = true;
+        }
+        assertTrue("Error in exporting file due to lack of storage space",
+            flagForException);
+    }
+
+     /**
+     * To Test Export :Two Media Items added
+     *
+     * @throws Exception
+     */
+    @LargeTest
+    public void testExportTwoVideos() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        final String videoItemFileName1 = INPUT_FILE_PATH +
+            "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+        final String outFilename = mVideoEditorHelper
+            .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        try {
+            final int[] progressUpdate = new int[100];
+            mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+                MediaProperties.BITRATE_800K, new ExportProgressListener() {
+                    int i = 0;
+                    public void onProgress(VideoEditor ve, String outFileName,
+                        int progress) {
+                            progressUpdate[i++] = progress;
+                    }
+                });
+            mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        } catch (Exception e) {
+            assertTrue("Error in Export" + e.toString(), false);
+        }
+        mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+            MediaProperties.HEIGHT_720, 0,
+            (mediaVideoItem.getDuration()+ mediaVideoItem1.getDuration()),
+            MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+        mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java
new file mode 100644 (file)
index 0000000..bd0a838
--- /dev/null
@@ -0,0 +1,1161 @@
+/*
+ * Copyright (C) 2011 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.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.Effect;
+import android.media.videoeditor.EffectColor;
+import android.media.videoeditor.EffectKenBurns;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.Overlay;
+import android.media.videoeditor.OverlayFrame;
+import android.media.videoeditor.Transition;
+import android.media.videoeditor.TransitionAlpha;
+import android.media.videoeditor.TransitionCrossfade;
+import android.media.videoeditor.TransitionFadeBlack;
+import android.media.videoeditor.TransitionSliding;
+import android.media.videoeditor.VideoEditor;
+import android.media.videoeditor.VideoEditor.ExportProgressListener;
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
+import android.media.videoeditor.VideoEditor.PreviewProgressListener;
+import android.media.videoeditor.VideoEditor.OverlayData;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.view.SurfaceHolder;
+
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.concurrent.TimeUnit;
+
+import android.util.Log;
+
+public class VideoEditorPreviewTest extends
+    ActivityInstrumentationTestCase<MediaFrameworkTest> {
+    private final String TAG = "VideoEditorTest";
+
+    private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+    private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+    private final String PROJECT_CLASS_NAME =
+        "android.media.videoeditor.VideoEditorImpl";
+
+    private VideoEditor mVideoEditor;
+
+    private VideoEditorHelper mVideoEditorHelper;
+
+    private class EventHandler extends Handler {
+        public EventHandler( Looper lp)
+        {
+            super(lp);
+        }
+        public void handleMessage(Message msg)
+        {
+            switch (msg.what)
+            {
+                default:
+                MediaFrameworkTest.testInvalidateOverlay();
+            }
+        }
+    }
+    private EventHandler mEventHandler;
+
+    private boolean previewStart;
+    private boolean previewStop;
+
+    /* Minimum waiting time for Semaphore to wait for release */
+    private final long minWaitingTime = 1000;
+
+    // Declares the annotation for Preview Test Cases
+    public @interface Preview {
+    }
+
+    public VideoEditorPreviewTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+
+        Looper looper;
+        if ((looper = Looper.myLooper()) != null) {
+            mEventHandler = new EventHandler(looper);
+
+        } else {
+            //Handle error when looper can not be created.
+            ;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        // setup for each test case.
+        super.setUp();
+        mVideoEditorHelper = new VideoEditorHelper();
+        // Create a random String which will be used as project path, where all
+        // project related files will be stored.
+        final String projectPath =
+            mVideoEditorHelper.createRandomFile(PROJECT_LOCATION);
+        mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+        // Clean the directory created as project path
+        mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+        System.gc();
+        super.tearDown();
+    }
+
+    protected void setPreviewStart() {
+        previewStart = true;
+    }
+    protected void setPreviewStop() {
+        previewStop = true;
+    }
+
+    protected void validatePreviewProgress(int startMs, int endMs,
+        boolean loop, long duration) throws Exception {
+
+        final int[] progressUpdate = new int[100];
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            int i = 0;
+            public void onProgress(Object item, int action, int progress) {
+                progressUpdate[i++] = progress;
+            }
+        });
+        mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+
+        long waitingTime = minWaitingTime;
+        if (endMs == -1) {
+            waitingTime += duration;
+        }
+        else {
+            waitingTime += (endMs - startMs);
+        }
+        blockTillPreviewCompletes.acquire();
+        try {
+        mVideoEditor.startPreview(surfaceHolder, startMs, endMs, loop, 1,
+            new PreviewProgressListener() {
+                public void onProgress(VideoEditor videoEditor, long timeMs,
+                    OverlayData overlayData) {
+
+                        if ( overlayData != null) {
+                            if(overlayData.needsRendering()) {
+                                overlayData.renderOverlay(MediaFrameworkTest.mDestBitmap);
+                                mEventHandler.sendMessage(mEventHandler.obtainMessage(1, 2, 3));
+                            }
+                        }
+                }
+                public void onStart(VideoEditor videoEditor) {
+                    setPreviewStart();
+                }
+                public void onStop(VideoEditor videoEditor) {
+                    setPreviewStop();
+                    blockTillPreviewCompletes.release();
+                }
+        });
+        } catch (Exception e) {
+            blockTillPreviewCompletes.release();
+        }
+        blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+
+        mVideoEditor.stopPreview();
+        assertTrue("Preview Failed to start", previewStart);
+        assertTrue("Preview Failed to stop", previewStop);
+
+        blockTillPreviewCompletes.release();
+    }
+
+    // -----------------------------------------------------------------
+    // Preview
+    // -----------------------------------------------------------------
+
+    /**
+     *To test Preview : FULL Preview of current work (beginning till end)
+     */
+    // TODO : remove TC_PRV_001
+    @LargeTest
+    public void testPreviewTheStoryBoard() throws Exception {
+        final String videoItemFileName1 = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+        final String videoItemFileName2 = INPUT_FILE_PATH
+            + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+        final String videoItemFileName3 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        previewStart = false;
+        previewStop = false;
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaVideoItem mediaVideoItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2",
+                videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem2);
+        mediaVideoItem2.setExtractBoundaries(0, 10000);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3",
+                videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem3.setExtractBoundaries(0, 10000);
+
+        mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId());
+        List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0));
+        assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1));
+        assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2));
+
+        mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+        assertEquals("Media Item 1 Rendering Mode",
+            MediaItem.RENDERING_MODE_BLACK_BORDER,
+            mediaVideoItem1.getRenderingMode());
+
+        mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+        assertEquals("Media Item 2 Rendering Mode",
+            MediaItem.RENDERING_MODE_BLACK_BORDER,
+            mediaVideoItem2.getRenderingMode());
+
+        mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH);
+        assertEquals("Media Item 3 Rendering Mode",
+            MediaItem.RENDERING_MODE_STRETCH,
+            mediaVideoItem3.getRenderingMode());
+
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3);
+        assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3,
+            mVideoEditor.getAspectRatio());
+
+        validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     * To test Preview : Preview of start + 10 sec till end of story board
+     */
+    // TODO : remove TC_PRV_002
+    @LargeTest
+    public void testPreviewTheStoryBoardFromDuration() throws Exception {
+        final String videoItemFileName1 = INPUT_FILE_PATH
+            + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+        final String videoItemFileName2 = INPUT_FILE_PATH +
+            "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+        final String videoItemFileName3 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaVideoItem mediaVideoItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2",
+                videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem2.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem2);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3",
+                videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem3.setExtractBoundaries(0, 10000);
+
+        mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId());
+
+        List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0));
+        assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1));
+        assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2));
+        mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        assertEquals("Media Item 1 Rendering Mode",
+            MediaItem.RENDERING_MODE_BLACK_BORDER,
+            mediaVideoItem1.getRenderingMode());
+        mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        assertEquals("Media Item 2 Rendering Mode",
+            MediaItem.RENDERING_MODE_BLACK_BORDER,
+            mediaVideoItem2.getRenderingMode());
+        mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH);
+
+        assertEquals("Media Item 3 Rendering Mode",
+            MediaItem.RENDERING_MODE_STRETCH,
+            mediaVideoItem3.getRenderingMode());
+
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3);
+        assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3,
+            mVideoEditor.getAspectRatio());
+
+        validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     * To test Preview : Preview of current Effects applied
+     */
+    // TODO : remove TC_PRV_003
+    @LargeTest
+    public void testPreviewOfEffects() throws Exception {
+        final String videoItemFileName1 = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final EffectColor effectNegative =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1,
+                "effectNegative", 0, 2000, EffectColor.TYPE_NEGATIVE, 0);
+        mediaVideoItem1.addEffect(effectNegative);
+
+        final EffectColor effectGreen =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectGreen",
+                2000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+        mediaVideoItem1.addEffect(effectGreen);
+
+        final EffectColor effectFifties =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1,
+                "effectFifties", 5000, 4000, EffectColor.TYPE_FIFTIES, 0);
+        mediaVideoItem1.addEffect(effectFifties);
+
+        List<Effect> effectList = mediaVideoItem1.getAllEffects();
+        assertEquals("Effect List Size", 3, effectList.size());
+        assertEquals("Effect negative", effectNegative, effectList.get(0));
+        assertEquals("Effect Green", effectGreen, effectList.get(1));
+        assertEquals("Effect Fifties", effectFifties, effectList.get(2));
+
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3);
+        assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_4_3,
+            mVideoEditor.getAspectRatio());
+
+        final long storyboardDuration = mVideoEditor.getDuration() ;
+        validatePreviewProgress(0, (int)(storyboardDuration/2), false, (storyboardDuration/2));
+
+        assertEquals("Removing Effect : Negative", effectNegative,
+            mediaVideoItem1.removeEffect(effectNegative.getId()));
+
+        effectList = mediaVideoItem1.getAllEffects();
+
+        assertEquals("Effect List Size", 2, effectList.size());
+        assertEquals("Effect Green", effectGreen, effectList.get(0));
+        assertEquals("Effect Fifties", effectFifties, effectList.get(1));
+
+        validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     *To test Preview : Preview of current Transitions applied (with multiple
+     * generatePreview)
+     */
+    // TODO : remove TC_PRV_004
+    @LargeTest
+    public void testPreviewWithTransition() throws Exception {
+
+        final String videoItemFileName1 = INPUT_FILE_PATH +
+            "H263_profile0_176x144_10fps_96kbps_0_25.3gp";
+        final String imageItemFileName1 = INPUT_FILE_PATH +
+            "IMG_1600x1200.jpg";
+        final String videoItemFileName2 = INPUT_FILE_PATH +
+            "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+        final String maskFilename = INPUT_FILE_PATH +
+            "TransitionSpiral_QVGA.jpg";
+        previewStart = false;
+        previewStop = false;
+
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaImageItem mediaImageItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFileName1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem1);
+
+        final MediaVideoItem mediaVideoItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem2.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem2);
+
+        final TransitionCrossfade transition1And2CrossFade =
+            mVideoEditorHelper.createTCrossFade("transition_1_2_CF",
+                mediaVideoItem1, mediaImageItem1, 2000,
+                Transition.BEHAVIOR_MIDDLE_FAST);
+        mVideoEditor.addTransition(transition1And2CrossFade);
+
+        final TransitionAlpha transition2And3Alpha =
+            mVideoEditorHelper.createTAlpha("transition_2_3", mediaImageItem1,
+                mediaVideoItem2, 4000, Transition.BEHAVIOR_SPEED_UP,
+                maskFilename, 50, true);
+        mVideoEditor.addTransition(transition2And3Alpha);
+
+        final TransitionFadeBlack transition1FadeBlack =
+            mVideoEditorHelper.createTFadeBlack("transition_1FB", null,
+                mediaVideoItem1, 2000, Transition.BEHAVIOR_MIDDLE_FAST);
+        mVideoEditor.addTransition(transition1FadeBlack);
+
+        List<Transition> transitionList = mVideoEditor.getAllTransitions();
+        assertEquals("Transition List Size", 3, transitionList.size());
+        assertEquals("Transition 1", transition1And2CrossFade,
+            transitionList.get(0));
+        assertEquals("Transition 2", transition2And3Alpha, transitionList.get(1));
+        assertEquals("Transition 3", transition1FadeBlack, transitionList.get(2));
+
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_3_2);
+
+        final int[] progressValues = new int[300];
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            int i = 0;
+
+            public void onProgress(Object item, int action, int progress) {
+                if (item instanceof TransitionCrossfade) {
+                    progressValues[i] = progress;
+                    assertEquals("Object", item, transition1And2CrossFade);
+                    assertEquals("Action", action,
+                        MediaProcessingProgressListener.ACTION_ENCODE);
+                } else if (item instanceof TransitionAlpha) {
+                    progressValues[i] = progress;
+                    assertEquals("Object", item, transition2And3Alpha);
+                    assertEquals("Action", action,
+                        MediaProcessingProgressListener.ACTION_ENCODE);
+                } else if (item instanceof TransitionFadeBlack) {
+                    progressValues[i] = progress;
+                    assertEquals("Object", item, transition1FadeBlack);
+                    assertEquals("Action", action,
+                        MediaProcessingProgressListener.ACTION_ENCODE);
+                }
+                i++;
+            }
+        });
+
+        mVideoEditorHelper.checkProgressCBValues(progressValues);
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+
+        long waitingTime = minWaitingTime + 10000;
+
+        blockTillPreviewCompletes.acquire();
+        try {
+        mVideoEditor.startPreview(surfaceHolder, 0, 10000, false, 1,
+            new PreviewProgressListener() {
+            public void onProgress(VideoEditor videoEditor, long timeMs,
+                OverlayData overlayData) {
+                }
+                public void onStart(VideoEditor videoEditor) {
+                    setPreviewStart();
+                }
+                public void onStop(VideoEditor videoEditor) {
+                    setPreviewStop();
+                    blockTillPreviewCompletes.release();
+                }
+        });
+        } catch (Exception e) {
+            blockTillPreviewCompletes.release();
+        }
+        blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+        mVideoEditor.stopPreview();
+        blockTillPreviewCompletes.release();
+        assertTrue("Preview Failed to start", previewStart);
+        assertTrue("Preview Failed to stop", previewStop);
+
+        assertEquals("Removing Transition " + transition1And2CrossFade.getId(),
+            transition1And2CrossFade,
+            mVideoEditor.removeTransition(transition1And2CrossFade.getId()));
+        transitionList = mVideoEditor.getAllTransitions();
+        assertEquals("Transition List Size", 2, transitionList.size());
+        assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0));
+        assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1));
+
+        validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+
+
+        final TransitionSliding transition1And2Sliding =
+            mVideoEditorHelper.createTSliding("transition_1_2Sliding",
+                mediaVideoItem1, mediaImageItem1, 4000,
+                Transition.BEHAVIOR_MIDDLE_FAST,
+                TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN);
+        mVideoEditor.addTransition(transition1And2Sliding);
+
+        transitionList = mVideoEditor.getAllTransitions();
+        assertEquals("Transition List Size", 3, transitionList.size());
+        assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0));
+        assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1));
+        assertEquals("Transition 3", transition1And2Sliding,
+            transitionList.get(2));
+
+        validatePreviewProgress(5000, -1, false, (mVideoEditor.getDuration()));
+
+    }
+
+    /**
+     * To test Preview : Preview of current Overlay applied
+     */
+    // TODO : remove TC_PRV_005
+    @LargeTest
+    public void testPreviewWithOverlay() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH
+            + "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp";
+        final String overlayFilename1 = INPUT_FILE_PATH +
+            "IMG_640x480_Overlay1.png";
+        final String overlayFilename2 = INPUT_FILE_PATH +
+            "IMG_640x480_Overlay2.png";
+        final int previewFrom = 5000;
+        final int previewTo = 10000;
+        final boolean previewLoop = false;
+        final int previewCallbackFrameCount = 1;
+        final int setAspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+        boolean flagForException = false;
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, renderingMode);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+        mediaVideoItem.setExtractBoundaries(0, 10000);
+
+        final Bitmap mBitmap1 =  mVideoEditorHelper.getBitmap(overlayFilename1,
+            640, 480);
+        final OverlayFrame overlayOnMvi1 =
+            mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi1",
+                mBitmap1, 0, 5000);
+        mediaVideoItem.addOverlay(overlayOnMvi1);
+
+        final Bitmap mBitmap2 =  mVideoEditorHelper.getBitmap(overlayFilename2,
+            640, 480);
+        final OverlayFrame overlayOnMvi2 =
+            mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi2",
+                mBitmap2, 5000, 9000);
+        mediaVideoItem.addOverlay(overlayOnMvi2);
+
+        List<Overlay> overlayList = mediaVideoItem.getAllOverlays();
+        assertEquals("Overlay Size", 2, overlayList.size());
+        assertEquals("Overlay 1", overlayOnMvi1, overlayList.get(0));
+        assertEquals("Overlay 2", overlayOnMvi2, overlayList.get(1));
+
+        mVideoEditor.setAspectRatio(setAspectRatio);
+
+        validatePreviewProgress(0 /* previewFrom */, -1, previewLoop,
+            mVideoEditor.getDuration());
+    }
+
+    /**
+     * To test Preview : Preview of current Trim applied (with default aspect
+     * ratio)
+     */
+    // TODO : remove TC_PRV_006
+    @LargeTest
+    public void testPreviewWithTrim() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, MediaItem.RENDERING_MODE_CROPPING);
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        boolean flagForException = false;
+        previewStart = false;
+        previewStop = false;
+        mediaVideoItem.setExtractBoundaries(mediaVideoItem.getDuration() / 2,
+            mediaVideoItem.getDuration());
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        validatePreviewProgress(1000, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     * To test Preview : Preview of current work having Overlay and Effect
+     * applied
+     */
+
+    // TODO : remove TC_PRV_007
+    @LargeTest
+    public void testPreviewWithOverlayEffectKenBurn() throws Exception {
+
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+        final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String videoItemFileName1 = INPUT_FILE_PATH +
+            "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4";
+        final String overlayFilename = INPUT_FILE_PATH +
+            "IMG_640x480_Overlay1.png";
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaImageItem mediaImageItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFileName, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem2);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem3);
+
+        final EffectColor effectColor =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem1, "Effect1",
+                1000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+        mediaVideoItem1.addEffect(effectColor);
+
+        final Rect startRect = new Rect((mediaImageItem2.getHeight() / 3),
+            (mediaImageItem2.getWidth() / 3), (mediaImageItem2.getHeight() / 2),
+            (mediaImageItem2.getWidth() / 2));
+        final Rect endRect = new Rect(0, 0, mediaImageItem2.getWidth(),
+            mediaImageItem2.getHeight());
+
+        final EffectKenBurns kbeffectOnMI2 = new EffectKenBurns(mediaImageItem2,
+            "KBOnM2", startRect, endRect, 0, 10000);
+        assertNotNull("EffectKenBurns", kbeffectOnMI2);
+        mediaImageItem2.addEffect(kbeffectOnMI2);
+
+        final Bitmap mBitmap =  mVideoEditorHelper.getBitmap(overlayFilename,
+            640, 480);
+        final OverlayFrame overlayFrame =
+            mVideoEditorHelper.createOverlay(mediaVideoItem3, "OverlayID",
+                mBitmap, (mediaImageItem2.getDuration() / 4),
+                (mediaVideoItem3.getDuration() / 3));
+        mediaVideoItem3.addOverlay(overlayFrame);
+
+        validatePreviewProgress(5000, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     *To test Preview : Export during preview
+     */
+    // TODO : remove TC_PRV_008
+    @LargeTest
+    public void testPreviewDuringExport() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        previewStart = false;
+        previewStop = false;
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(0, 20000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        long waitingTime = minWaitingTime + mVideoEditor.getDuration();
+
+        blockTillPreviewCompletes.acquire();
+
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+        try {
+            mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1,
+                new PreviewProgressListener() {
+                    final String fileName = mVideoEditor.getPath() + "\test.3gp";
+                    final int height = MediaProperties.HEIGHT_360;
+                    final int bitrate = MediaProperties.BITRATE_512K;
+                    public void onProgress(VideoEditor videoEditor, long timeMs,
+                        OverlayData overlayData) {
+                            if (timeMs >= 10000)
+                            try {
+                                videoEditor.export(fileName, height, bitrate,
+                                    new ExportProgressListener() {
+                                        public void onProgress(VideoEditor ve,
+                                            String outFileName,int progress) {
+
+                                        }
+                                    });
+                            } catch (IOException e) {
+                                assertTrue("UnExpected Error in Export" +
+                                    e.toString(), false);
+                        }
+                    }
+                public void onStart(VideoEditor videoEditor) {
+                    setPreviewStart();
+                }
+                public void onStop(VideoEditor videoEditor) {
+                    setPreviewStop();
+                    blockTillPreviewCompletes.release();
+                }
+            });
+        } catch (Exception e) {
+            blockTillPreviewCompletes.release();
+        }
+
+        blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+        mVideoEditor.stopPreview();
+        assertTrue("Preview Failed to start", previewStart);
+        assertTrue("Preview Failed to stop", previewStop);
+        blockTillPreviewCompletes.release();
+    }
+
+    /**
+     * To test Preview : Preview of current Effects applied (with from time >
+     * total duration)
+     */
+    // TODO : remove TC_PRV_009
+    @LargeTest
+    public void testPreviewWithDurationGreaterThanMediaDuration()
+        throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+        final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+        boolean flagForException = false;
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName, renderingMode);
+        try {
+            mediaVideoItem1.setExtractBoundaries(0, 20000);
+        } catch (Exception e) {
+            assertTrue("Exception during setExtract Boundaries", false);
+        }
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+        long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 30000);
+        if(waitingTime < 0)
+        {
+            waitingTime = minWaitingTime;
+        }
+
+        blockTillPreviewCompletes.acquire();
+        try {
+            mVideoEditor.startPreview(surfaceHolder, 30000, -1, true, 1,
+            new PreviewProgressListener() {
+                public void onProgress(VideoEditor videoEditor, long timeMs,
+                    OverlayData overlayData) {
+            }
+                public void onStart(VideoEditor videoEditor) {
+                    setPreviewStart();
+                }
+                public void onStop(VideoEditor videoEditor) {
+                    setPreviewStop();
+                    blockTillPreviewCompletes.release();
+                }
+        });
+
+        } catch (IllegalArgumentException e) {
+            blockTillPreviewCompletes.release();
+            flagForException = true;
+        }
+        blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+        assertTrue("Expected Error in Preview", flagForException);
+        mVideoEditor.stopPreview();
+        blockTillPreviewCompletes.release();
+    }
+
+    /**
+     * To test Preview : Preview of current Effects applied (with Render Preview
+     * Frame)
+     */
+    // TODO : remove TC_PRV_010
+    @LargeTest
+    public void testPreviewWithRenderPreviewFrame() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+        boolean flagForException = false;
+        OverlayData overlayData1 = new OverlayData();
+        previewStart = false;
+        previewStop = false;
+
+        final String overlayFilename1 = INPUT_FILE_PATH +
+            "IMG_640x480_Overlay1.png";
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor,
+            "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final EffectColor effectPink =
+            mVideoEditorHelper.createEffectItem(mediaVideoItem,
+                "effectNegativeOnMvi", 1000, 3000, EffectColor.TYPE_COLOR,
+                 EffectColor.PINK);
+        mediaVideoItem.addEffect(effectPink);
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+
+        assertEquals("Render preview Frame at 5 Sec", 5000,
+            mVideoEditor.renderPreviewFrame(surfaceHolder, 5000,
+            overlayData1));
+
+        assertEquals("Render preview Frame at 7 Sec", 7000,
+            mVideoEditor.renderPreviewFrame(surfaceHolder, 7000,
+            overlayData1));
+
+        long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 5000);
+
+        blockTillPreviewCompletes.acquire();
+        try {
+            mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1,
+                new PreviewProgressListener() {
+                    public void onProgress(VideoEditor videoEditor, long timeMs,
+                        OverlayData overlayData) {
+                    }
+                    public void onStart(VideoEditor videoEditor) {
+                        setPreviewStart();
+                    }
+                    public void onStop(VideoEditor videoEditor) {
+                        setPreviewStop();
+                        blockTillPreviewCompletes.release();
+                    }
+            });
+        } catch (Exception e) {
+            blockTillPreviewCompletes.release();
+        }
+        blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+        mVideoEditor.stopPreview();
+        assertTrue("Preview Failed to start", previewStart);
+        assertTrue("Preview Failed to stop", previewStop);
+        blockTillPreviewCompletes.release();
+    }
+
+    /**
+     * To test Preview : Preview of current work from selected jump location
+     * till end with Audio Track
+     */
+    // TODO : remove TC_PRV_011
+    @LargeTest
+    public void testPreviewWithEndAudioTrack() throws Exception {
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+        final String audioFilename = INPUT_FILE_PATH +
+            "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+
+        boolean flagForException = false;
+        previewStart = false;
+        previewStop = false;
+        final MediaImageItem mediaImageItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                imageItemFilename1, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem1);
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem.setExtractBoundaries(1000, 8000);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final MediaImageItem mediaImageItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem2);
+
+        final AudioTrack audioTrack =
+            mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename);
+        mVideoEditor.addAudioTrack(audioTrack);
+
+        List<AudioTrack> audioList = mVideoEditor.getAllAudioTracks();
+        assertEquals("Audio Track List size", 1, audioList.size());
+        assertEquals("Audio Track", audioTrack, audioList.get(0));
+        mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3);
+
+        validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration());
+    }
+
+    /**
+     * To test render Preview Frame
+     */
+    // TODO : remove TC_PRV_012
+    @LargeTest
+    public void testRenderPreviewFrame() throws Exception {
+        final String videoItemFileName1 = INPUT_FILE_PATH
+            + "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+        final String videoItemFileName2 = INPUT_FILE_PATH
+            + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+        final String videoItemFileName3 = INPUT_FILE_PATH
+            + "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        final String imageItemFilename1 = INPUT_FILE_PATH
+            + "IMG_1600x1200.jpg";
+        final String imageItemFilename2 = INPUT_FILE_PATH
+            + "IMG_176x144.jpg";
+        final String audioFilename = INPUT_FILE_PATH
+            + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+        OverlayData overlayData1 = new OverlayData();
+        previewStart = false;
+        previewStop = false;
+        final MediaVideoItem mediaVideoItem1 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(0, 10000);
+        mVideoEditor.addMediaItem(mediaVideoItem1);
+
+        final MediaVideoItem mediaVideoItem2 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 4,
+            mediaVideoItem2.getDuration() / 2);
+        mVideoEditor.addMediaItem(mediaVideoItem2);
+
+        final MediaVideoItem mediaVideoItem3 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+                videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 2,
+            mediaVideoItem2.getDuration());
+        mVideoEditor.addMediaItem(mediaVideoItem3);
+
+        final MediaImageItem mediaImageItem4 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+                imageItemFilename1, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        final MediaImageItem mediaImageItem5 =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+                imageItemFilename2, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+        List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item List Size", 3, mediaList.size());
+
+        mVideoEditor.insertMediaItem(mediaImageItem4, mediaVideoItem2.getId());
+        mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item List Size", 4, mediaList.size());
+        assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0));
+        assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1));
+        assertEquals("Media item 4", mediaImageItem4, mediaList.get(2));
+        assertEquals("Media item 3", mediaVideoItem3, mediaList.get(3));
+
+        mVideoEditor.insertMediaItem(mediaImageItem5, mediaImageItem4.getId());
+        mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item List Size", 5, mediaList.size());
+        assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0));
+        assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1));
+        assertEquals("Media item 4", mediaImageItem4, mediaList.get(2));
+        assertEquals("Media item 5", mediaImageItem5, mediaList.get(3));
+        assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4));
+
+        mVideoEditor.moveMediaItem(mediaVideoItem1.getId(),
+            mediaImageItem5.getId());
+        mediaList = mVideoEditor.getAllMediaItems();
+        assertEquals("Media Item List Size", 5, mediaList.size());
+        assertEquals("Media item 2", mediaVideoItem2, mediaList.get(0));
+        assertEquals("Media item 4", mediaImageItem4, mediaList.get(1));
+        assertEquals("Media item 5", mediaImageItem5, mediaList.get(2));
+        assertEquals("Media item 1", mediaVideoItem1, mediaList.get(3));
+        assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4));
+
+        final TransitionCrossfade transition2And4CrossFade =
+            mVideoEditorHelper.createTCrossFade("transition2And4CrossFade",
+                mediaVideoItem2, mediaImageItem4, 2000,
+                Transition.BEHAVIOR_MIDDLE_FAST);
+        mVideoEditor.addTransition(transition2And4CrossFade);
+
+        final TransitionCrossfade transition1And3CrossFade =
+            mVideoEditorHelper.createTCrossFade("transition1And3CrossFade",
+                mediaVideoItem1, mediaVideoItem3, 5000,
+                Transition.BEHAVIOR_MIDDLE_FAST);
+        mVideoEditor.addTransition(transition1And3CrossFade);
+
+        final AudioTrack audioTrack =
+            mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename);
+        audioTrack.setExtractBoundaries(0, 2000);
+        mVideoEditor.addAudioTrack(audioTrack);
+
+        audioTrack.enableLoop();
+
+        mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+            public void onProgress(Object item, int action, int progress) {
+            }
+        });
+
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+
+        mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/4, overlayData1);
+        Thread.sleep(1000);
+        mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/2, overlayData1);
+        Thread.sleep(1000);
+        mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration(), overlayData1);
+
+    }
+
+    /**
+     * To Test Preview : Without any Media Items in the story Board
+     */
+    // TODO : remove TC_PRV_013
+    @LargeTest
+    public void testStartPreviewWithoutMediaItems() throws Exception {
+        boolean flagForException = false;
+
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+        try{
+            mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1,
+                new PreviewProgressListener() {
+                    public void onProgress(VideoEditor videoEditor, long timeMs,
+                        OverlayData overlayData) {
+                    }
+                    public void onStart(VideoEditor videoEditor) {
+                        setPreviewStart();
+                    }
+                    public void onStop(VideoEditor videoEditor) {
+                        setPreviewStop();
+                }
+            });
+        }catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Preview without Media Items", flagForException);
+    }
+
+    /**
+     * To Test Preview : Add Media and Remove Media Item (Without any Media
+     * Items in the story Board)
+     */
+    // TODO : remove TC_PRV_014
+    @LargeTest
+    public void testStartPreviewAddRemoveMediaItems() throws Exception {
+        final String videoItemFilename1 = INPUT_FILE_PATH
+            + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+        final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+        final String alphaFilename = INPUT_FILE_PATH +
+            "TransitionSpiral_QVGA.jpg";
+        boolean flagForException = false;
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+                videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mediaVideoItem.setExtractBoundaries(0, 15000);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final MediaImageItem mediaImageItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+                imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaImageItem);
+
+        final TransitionAlpha transition1And2 =
+            mVideoEditorHelper.createTAlpha("transition", mediaVideoItem,
+                mediaImageItem, 3000, Transition.BEHAVIOR_SPEED_UP,
+                alphaFilename, 10, false);
+        mVideoEditor.addTransition(transition1And2);
+
+        final EffectColor effectColor =
+            mVideoEditorHelper.createEffectItem(mediaImageItem, "effect", 5000,
+                3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+        mediaImageItem.addEffect(effectColor);
+
+        assertEquals("removing Media item 1", mediaVideoItem,
+            mVideoEditor.removeMediaItem(mediaVideoItem.getId()));
+        assertEquals("removing Media item 2", mediaImageItem,
+            mVideoEditor.removeMediaItem(mediaImageItem.getId()));
+
+        try{
+            mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+                public void onProgress(Object item, int action, int progress) {
+                }
+            });
+            final SurfaceHolder surfaceHolder =
+                MediaFrameworkTest.mSurfaceView.getHolder();
+            mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1,
+                new PreviewProgressListener() {
+                    public void onProgress(VideoEditor videoEditor, long timeMs,
+                        OverlayData overlayData) {
+                    }
+                    public void onStart(VideoEditor videoEditor) {
+                        setPreviewStart();
+                    }
+                    public void onStop(VideoEditor videoEditor) {
+                        setPreviewStop();
+                }
+            });
+        }catch (IllegalArgumentException e) {
+            flagForException = true;
+        }
+        assertTrue("Preview with removed Media Items", flagForException);
+
+    }
+
+    /**
+     * To test Preview : Preview of current Effects applied (with Render Preview
+     * Frame)
+     */
+    // TODO : remove TC_PRV_015
+    @LargeTest
+    public void testPreviewWithRenderPreviewFrameWithoutGenerate() throws Exception {
+        final String videoItemFileName = INPUT_FILE_PATH +
+            "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+        boolean flagForException = false;
+        long duration = 0;
+        OverlayData overlayData1 = new OverlayData();
+
+        final MediaVideoItem mediaVideoItem =
+            mVideoEditorHelper.createMediaItem(mVideoEditor,
+            "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+        mVideoEditor.addMediaItem(mediaVideoItem);
+
+        final SurfaceHolder surfaceHolder =
+            MediaFrameworkTest.mSurfaceView.getHolder();
+        duration = mVideoEditor.getDuration();
+        /* RenderPreviewFrame returns -1 to indicate last frame */
+        try {
+        assertEquals("Render preview Frame at item duration", -1,
+            mVideoEditor.renderPreviewFrame(surfaceHolder, duration,
+            overlayData1));
+        } catch ( Exception e) {
+            assertTrue (" Render Preview Frame without generate", false);
+        }
+        duration = mVideoEditor.getDuration() + 1000;
+        try {
+            mVideoEditor.renderPreviewFrame(surfaceHolder, duration,
+            overlayData1);
+        } catch ( IllegalStateException e) {
+            flagForException = true;
+        }
+        assertTrue (" Preview time greater than duration", flagForException);
+    }
+
+}
diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4
new file mode 100755 (executable)
index 0000000..be050dc
Binary files /dev/null and b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 differ
old mode 100644 (file)
new mode 100755 (executable)
index 6611986..0f32131
Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png and b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png differ
diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp
deleted file mode 100644 (file)
index bd8b079..0000000
Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp and /dev/null differ
diff --git a/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp
new file mode 100644 (file)
index 0000000..02103c6
--- /dev/null
@@ -0,0 +1 @@
+This is a text file
\ No newline at end of file
index 3fdfdbb..c358e13 100644 (file)
@@ -40,6 +40,9 @@
         android:layout_alignParentTop="true"
         android:layout_marginLeft="123dip"
         android:layout_marginTop="16dip"
+        android:maxWidth="64dip"
+        android:maxHeight="64dip"
+        android:adjustViewBounds="true"
     />
 
     <View android:id="@+id/recents_callout_line"
index b1e74ad..ce0848b 100644 (file)
@@ -90,8 +90,8 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel,
             if (imi2 == null) return 0;
             if (imi1 == null) return 1;
             if (mPackageManager != null) {
-                CharSequence imiId1 = imi1.loadLabel(mPackageManager);
-                CharSequence imiId2 = imi2.loadLabel(mPackageManager);
+                CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId();
+                CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId();
                 if (imiId1 != null && imiId2 != null) {
                     return imiId1.toString().compareTo(imiId2.toString());
                 }
index 6c6c2cc..018fe0c 100644 (file)
@@ -361,10 +361,12 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
 
     /** {@inheritDoc} */
     public void cleanUp() {
+        if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
         mUpdateMonitor.removeCallback(this);
         mLockPatternUtils = null;
         mUpdateMonitor = null;
         mCallback = null;
+        mLockPatternView.setOnPatternListener(null);
     }
 
     @Override
@@ -406,6 +408,7 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
                 mCallback.keyguardDone(true);
                 mCallback.reportSuccessfulUnlockAttempt();
             } else {
+                boolean reportFailedAttempt = false;
                 if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
                     mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
                 }
@@ -413,9 +416,10 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
                 if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                     mTotalFailedPatternAttempts++;
                     mFailedPatternAttemptsSinceLastTimeout++;
-                    mCallback.reportFailedUnlockAttempt();
+                    reportFailedAttempt = true;
                 }
-                if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+                if (mFailedPatternAttemptsSinceLastTimeout
+                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                     handleAttemptLockout(deadline);
                 } else {
@@ -427,6 +431,12 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
                             mCancelPatternRunnable,
                             PATTERN_CLEAR_TIMEOUT_MS);
                 }
+
+                // Because the following can result in cleanUp() being called on this screen,
+                // member variables reset in cleanUp() shouldn't be accessed after this call.
+                if (reportFailedAttempt) {
+                    mCallback.reportFailedUnlockAttempt();
+                }
             }
         }
     }
index d25b9c8..ba0f31b 100644 (file)
@@ -1949,14 +1949,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
 
     private boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
         if (subtype == null) return true;
-        String[] extraValues = subtype.getExtraValue().split(",");
-        final int N = extraValues.length;
-        for (int i = 0; i < N; ++i) {
-            if (SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME.equals(extraValues[i])) {
-                return false;
-            }
-        }
-        return true;
+        return subtype.containsExtraValueKey(SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME);
     }
 
     private void saveCurrentInputMethodAndSubtypeToHistory() {
index b7a276f..b662c55 100644 (file)
@@ -5889,7 +5889,7 @@ public class WindowManagerService extends IWindowManager.Stub
                         outSurface.copyFrom(surface);
                         final IBinder winBinder = window.asBinder();
                         token = new Binder();
-                        mDragState = new DragState(token, surface, flags, winBinder);
+                        mDragState = new DragState(token, surface, /*flags*/ 0, winBinder);
                         mDragState.mSurface = surface;
                         token = mDragState.mToken = new Binder();
 
index 41f3b23..a9efc98 100755 (executable)
@@ -305,8 +305,15 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
         fileIds = mPbrFile.mFileIds.get(recNum);
         if (fileIds == null || fileIds.isEmpty()) return;
 
+
+        int extEf = 0;
+        // Only call fileIds.get while EFEXT1_TAG is available
+        if (fileIds.containsKey(USIM_EFEXT1_TAG)) {
+            extEf = fileIds.get(USIM_EFEXT1_TAG);
+        }
+
         mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG),
-            fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
+            extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
         try {
             mLock.wait();
         } catch (InterruptedException e) {